diff --git a/Cargo.lock b/Cargo.lock index c9af449..5e11f71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "FreePPS" -version = "1.6.0" +version = "1.7.0" dependencies = [ "android_logger", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index f11d82b..5b8c22c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "FreePPS" -version = "1.6.0" +version = "1.7.0" edition = "2024" authors = ["Seyud"] @@ -23,4 +23,3 @@ opt-level = 1 debug = true lto = false codegen-units = 256 - diff --git a/docs/README.md b/docs/README.md index 354bb1a..ebde24c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,16 +16,19 @@ FreePPS 是一个专为米系设备设计的模块,能够**解锁并启用公版 PPS(Programmable Power Supply)快充协议支持**,让你的设备享受更好的兼容性! -> ⚠️ **重要注意事项**:锁定PPS支持模式,部分机型将**自动关闭MIPPS(小米私有PPS协议)支持**,两种协议存在冲突,无法同时启用。请根据您的充电设备选择适合的协议。 +> ⚠️ **重要注意事项**:自动识别目前仅在高通平台验证。联发科平台选择自动模式时仍按“锁定公版 PPS”处理。 > 💡 **特别感谢**:酷安@低线阻狂魔、酷安@花橋桥 提供的节点 ## 🚀 主要功能 - ✅ **PPS协议解锁** - 启用公版PPS快充支持 -- 🔄 **双模切换** - 通过模块操作按钮进行开/关切换 +- 🔄 **自动协议识别(高通)** - 插入时优先尝试小米协议;若只检测到公版 PD PPS,则自动执行一次软件重连并启用公版 PPS +- 🎛️ **三种模式** - 模块操作按钮依次切换“小米协议优先 → 锁定公版 PPS → 自动识别” - 🔛 **临时控制** - 使用模块开关进行兼容性切换PPS支持状态 +自动模式仅在每次插入后的短暂协商阶段使用定时器,空闲时由内核事件唤醒,不进行周期轮询。为保证下一次插入能优先完成小米认证,请保持自动模式并在切换充电器前正常拔出当前充电器。 + ## 🙏 致谢 diff --git a/docs/en/README.md b/docs/en/README.md index 2863f8b..5bb9215 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -15,16 +15,19 @@ FreePPS is a module specifically designed for Xiaomi/Mi devices that can **unlock and enable public PPS (Programmable Power Supply) fast charging protocol support**, giving your device better compatibility! -> ⚠️ **Important Note**: After enabling public PPS support, some devices will **automatically disable MIPPS (Xiaomi proprietary PPS protocol) support**, the two protocols conflict and cannot be enabled simultaneously. Please choose the appropriate protocol based on your charging device. +> ⚠️ **Important Note**: Automatic detection is currently verified only on Qualcomm devices. On MediaTek, Automatic mode continues to behave like Force Public PPS. > 💡 **Special Thanks**: Node provided by Coolapk @低线阻狂魔 and Coolapk @花橋桥 ## 🚀 Key Features - ✅ **PPS Protocol Unlock** - Enable public PPS fast charging support -- 🔄 **Two-state Toggle** - On/Off switching through module operation buttons +- 🔄 **Automatic protocol detection (Qualcomm)** - Prefer Xiaomi charging on attachment, then perform one software reconnect for public-PPS-only chargers +- 🎛️ **Three modes** - The module action button cycles through Xiaomi Priority, Force Public PPS, and Automatic Detection - 🔛 **Temporary Control** - Use module switches for compatibility switching of PPS support status +Automatic mode uses deadlines only during the brief negotiation after attachment. While idle, it sleeps until a kernel event instead of polling. Leave the module in Automatic mode and disconnect normally before changing chargers so the next attachment starts with Xiaomi authentication enabled. + ## 🙏 Acknowledgments - **Coolapk @低线阻狂魔**, **Coolapk @花橋桥** - Node diff --git a/module/action.sh b/module/action.sh index 0152255..c6e4813 100644 --- a/module/action.sh +++ b/module/action.sh @@ -2,6 +2,7 @@ MODDIR=${0%/*} FREE_FILE="$MODDIR/free" +AUTO_FILE="$MODDIR/auto" if [ -f "$FREE_FILE" ]; then FREE_VALUE=$(cat "$FREE_FILE" | tr -d '[:space:]') @@ -10,11 +11,16 @@ else fi if [ "$FREE_VALUE" = "0" ]; then + rm -f "$AUTO_FILE" printf "1" > "$FREE_FILE" echo "✅锁定PPS支持⚡" +elif [ ! -f "$AUTO_FILE" ]; then + touch "$AUTO_FILE" + echo "🔄协议自动识别💡" else printf "0" > "$FREE_FILE" - echo "⏸️PPS已暂停💤" + rm -f "$AUTO_FILE" + echo "⏸️小米协议优先💤" fi sleep 0.3 diff --git a/module/auto b/module/auto new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/module/auto @@ -0,0 +1 @@ + diff --git a/module/bin/FreePPS b/module/bin/FreePPS index 5ced197..025b6cf 100644 Binary files a/module/bin/FreePPS and b/module/bin/FreePPS differ diff --git a/module/customize.sh b/module/customize.sh index 22fd3d7..10f32b9 100644 --- a/module/customize.sh +++ b/module/customize.sh @@ -5,10 +5,9 @@ MODDIR=${0%/*} # Function to detect key press (Volume Up or Volume Down) or timeout detect_key_press() { timeout_seconds=6 + line=$(timeout "$timeout_seconds" getevent -ql | awk '/KEY_VOLUME/ {print; exit}') - read -r -t $timeout_seconds line < <(getevent -ql | awk '/KEY_VOLUME/ {print; exit}') - - if [ $? -eq 142 ]; then + if [ -z "$line" ]; then echo "[!] No key pressed within $timeout_seconds seconds. Skipping installation..." return 1 fi diff --git a/module/module.prop b/module/module.prop index bff238c..78c75ff 100644 --- a/module/module.prop +++ b/module/module.prop @@ -1,7 +1,7 @@ id=FreePPS name=FreePPS -version=v1.6.0 -versionCode=20260128 +version=v1.7.0 +versionCode=20260816 author=酷安@瓦力喀/GitHub@Seyud description=解锁并启用米系设备的公版 PPS 快充协议支持。(感谢“酷安@低线阻狂魔”、“酷安@花橋桥”提供节点) updateJson=https://gitee.com/Seyud/FreePPS/raw/master/Update.json diff --git a/src/common/constants.rs b/src/common/constants.rs index 75f1c2d..638b678 100644 --- a/src/common/constants.rs +++ b/src/common/constants.rs @@ -12,12 +12,16 @@ pub const IN_DELETE: u32 = 0x00000200; #[cfg(unix)] pub const MODULE_BASE_PATH: &str = "/data/adb/modules/FreePPS"; pub const FREE_FILE: &str = "/data/adb/modules/FreePPS/free"; +pub const AUTO_FILE: &str = "/data/adb/modules/FreePPS/auto"; pub const DISABLE_FILE: &str = "/data/adb/modules/FreePPS/disable"; #[cfg(unix)] pub const MODULE_PROP: &str = "/data/adb/modules/FreePPS/module.prop"; pub const PD_VERIFIED_PATH: &str = "/sys/class/qcom-battery/pd_verifed"; pub const PD_ADAPTER_VERIFIED_PATH: &str = "/sys/class/Charging_Adapter/pd_adapter/usbpd_verifed"; pub const BATTERY_STATUS_PATH: &str = "/sys/class/power_supply/battery/status"; +pub const INPUT_SUSPEND_PATH: &str = "/sys/class/qcom-battery/input_suspend"; +pub const TYPEC_MODE_PATH: &str = "/sys/class/qcom-battery/typec_mode"; +pub const USB_REAL_TYPE_PATH: &str = "/sys/class/qcom-battery/usb_real_type"; // 金标动画广播伪造相关 sysfs 节点 #[cfg(unix)] diff --git a/src/main.rs b/src/main.rs index 6d8a119..46ee86f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,11 +4,12 @@ mod pd; mod platform; use std::sync::Arc; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::{AtomicBool, AtomicU8}; use std::thread; +#[cfg(not(unix))] use std::time::Duration; -use common::constants::{FREE_FILE, PD_ADAPTER_VERIFIED_PATH, PD_VERIFIED_PATH}; +use common::constants::{PD_ADAPTER_VERIFIED_PATH, PD_VERIFIED_PATH}; use common::utils; use log::{error, info}; use monitoring::{ @@ -16,7 +17,7 @@ use monitoring::{ spawn_pd_adapter_verified_monitor, spawn_pd_verified_monitor, }; use pd::{PdAdapterVerifier, PdVerifier}; -use platform::install_signal_handlers; +use platform::{EventFd, SignalWaiter, install_signal_handlers}; fn main() { // 初始化 Android Logger @@ -33,44 +34,58 @@ fn main() { let module_manager = Arc::new(ModuleManager::new().expect("创建模块管理器失败")); // 初始化阶段:确保基础文件存在并设置初始状态 - if let Err(e) = module_manager.initialize_module() { + let initial_mode = module_manager.initialize_module().unwrap_or_else(|e| { error!("模块初始化失败: {}", e); - } + monitoring::ChargingMode::Native + }); // 创建运行标志 let running = Arc::new(AtomicBool::new(true)); - install_signal_handlers(&running); + let signal_waiter = SignalWaiter::new().expect("创建信号事件失败"); + install_signal_handlers(&running, &signal_waiter); let pd_verifier = Arc::new(PdVerifier::new().expect("创建PD验证器失败")); let pd_adapter_verifier = Arc::new(PdAdapterVerifier::new().expect("创建PD适配器验证器失败")); - let free_enabled = Arc::new(AtomicBool::new( - monitoring::FileMonitor::read_file_content(FREE_FILE).unwrap_or_else(|_| "0".to_string()) - == "1", - )); + let charging_mode = Arc::new(AtomicU8::new(initial_mode as u8)); + let qcom_config_event = Arc::new(EventFd::new().expect("创建qcom配置事件失败")); + let mtk_config_event = Arc::new(EventFd::new().expect("创建mtk配置事件失败")); let mut thread_handles: Vec> = Vec::new(); + let mut stop_events: Vec> = Vec::new(); // 创建free文件监控线程 + let free_stop_event = Arc::new(EventFd::new().expect("创建free线程停止事件失败")); + stop_events.push(Arc::clone(&free_stop_event)); thread_handles.push(spawn_free_file_monitor( Arc::clone(&running), Arc::clone(&module_manager), - Arc::clone(&free_enabled), + Arc::clone(&charging_mode), + Arc::clone(&qcom_config_event), + Arc::clone(&mtk_config_event), + free_stop_event, )); // 创建disable文件监控线程 + let disable_stop_event = Arc::new(EventFd::new().expect("创建disable线程停止事件失败")); + stop_events.push(Arc::clone(&disable_stop_event)); thread_handles.push(spawn_disable_file_monitor( Arc::clone(&running), Arc::clone(&module_manager), + disable_stop_event, )); // 初始化时按节点存在性一次性创建 qcom/mtk 线程(不做后续轮询判断/重启) if std::path::Path::new(PD_VERIFIED_PATH).exists() { info!("检测到qcom节点存在,启动qcom线程: {}", PD_VERIFIED_PATH); + let qcom_stop_event = Arc::new(EventFd::new().expect("创建qcom线程停止事件失败")); + stop_events.push(Arc::clone(&qcom_stop_event)); thread_handles.push(spawn_pd_verified_monitor( Arc::clone(&running), Arc::clone(&pd_verifier), - Arc::clone(&free_enabled), + Arc::clone(&charging_mode), + Arc::clone(&qcom_config_event), + qcom_stop_event, )); } else { info!("qcom节点不存在,跳过qcom线程启动: {}", PD_VERIFIED_PATH); @@ -81,10 +96,14 @@ fn main() { "检测到mtk节点存在,启动mtk线程: {}", PD_ADAPTER_VERIFIED_PATH ); + let mtk_stop_event = Arc::new(EventFd::new().expect("创建mtk线程停止事件失败")); + stop_events.push(Arc::clone(&mtk_stop_event)); thread_handles.push(spawn_pd_adapter_verified_monitor( Arc::clone(&running), Arc::clone(&pd_adapter_verifier), - Arc::clone(&free_enabled), + Arc::clone(&charging_mode), + Arc::clone(&mtk_config_event), + mtk_stop_event, )); } else { info!( @@ -94,9 +113,16 @@ fn main() { } info!( - "[{}] 监控线程已按需启动(仅初始化判断一次),主线程park等待...", + "[{}] 监控线程已按需启动(仅初始化判断一次),主线程等待退出信号...", main_thread_name ); + + #[cfg(unix)] + if let Err(error) = signal_waiter.wait() { + error!("等待退出信号失败: {}", error); + } + + #[cfg(not(unix))] while running.load(std::sync::atomic::Ordering::Relaxed) { thread::park_timeout(Duration::from_secs(1)); } @@ -104,6 +130,12 @@ fn main() { info!("检测到退出信号,开始停止所有监控线程..."); running.store(false, std::sync::atomic::Ordering::Relaxed); + for stop_event in &stop_events { + if let Err(error) = stop_event.notify() { + error!("通知监控线程停止失败: {}", error); + } + } + for handle in thread_handles { if let Err(e) = handle.join() { error!("线程join失败: {:?}", e); diff --git a/src/monitoring.rs b/src/monitoring.rs index 325f16f..e0fc44e 100644 --- a/src/monitoring.rs +++ b/src/monitoring.rs @@ -1,8 +1,10 @@ pub mod file_monitor; +pub mod mode; pub mod module_manager; pub mod threads; pub use file_monitor::FileMonitor; +pub use mode::ChargingMode; pub use module_manager::ModuleManager; pub use threads::{ spawn_disable_file_monitor, spawn_free_file_monitor, spawn_pd_adapter_verified_monitor, diff --git a/src/monitoring/mode.rs b/src/monitoring/mode.rs new file mode 100644 index 0000000..85d1975 --- /dev/null +++ b/src/monitoring/mode.rs @@ -0,0 +1,42 @@ +use anyhow::Result; +use std::path::Path; + +use crate::common::constants::{AUTO_FILE, FREE_FILE}; +use crate::monitoring::FileMonitor; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum ChargingMode { + Native = 0, + ForcePublicPps = 1, + Automatic = 2, +} + +impl ChargingMode { + pub fn from_files() -> Result { + let free = FileMonitor::read_file_content(FREE_FILE)?; + Ok(if free != "1" { + Self::Native + } else if Path::new(AUTO_FILE).exists() { + Self::Automatic + } else { + Self::ForcePublicPps + }) + } + + pub fn from_raw(value: u8) -> Self { + match value { + 1 => Self::ForcePublicPps, + 2 => Self::Automatic, + _ => Self::Native, + } + } + + pub fn description_prefix(self) -> &'static str { + match self { + Self::Native => "[⏸️小米协议优先💤] ", + Self::ForcePublicPps => "[✅锁定PPS支持⚡] ", + Self::Automatic => "[🔄协议自动识别💡] ", + } + } +} diff --git a/src/monitoring/module_manager.rs b/src/monitoring/module_manager.rs index d98f434..62876f5 100644 --- a/src/monitoring/module_manager.rs +++ b/src/monitoring/module_manager.rs @@ -1,12 +1,8 @@ use crate::common::FreePPSError; #[cfg(unix)] -use crate::common::constants::BATTERY_STATUS_PATH; -#[cfg(unix)] -use crate::common::constants::MODULE_PROP; -#[cfg(unix)] -use crate::common::constants::PD_ADAPTER_VERIFIED_PATH; +use crate::common::constants::{BATTERY_STATUS_PATH, MODULE_PROP, PD_ADAPTER_VERIFIED_PATH}; use crate::common::constants::{DISABLE_FILE, FREE_FILE, PD_VERIFIED_PATH}; -use crate::monitoring::FileMonitor; +use crate::monitoring::{ChargingMode, FileMonitor}; #[cfg(unix)] use crate::pd::PdAdapterVerifier; use crate::pd::PdVerifier; @@ -16,111 +12,56 @@ use std::fs; use std::path::Path; use std::sync::Mutex; -/// 模块状态管理器 pub struct ModuleManager { - // 缓存最后一次处理的状态 - last_state: Mutex, + last_mode: Mutex>, } impl ModuleManager { pub fn new() -> Result { Ok(Self { - last_state: Mutex::new(String::new()), + last_mode: Mutex::new(None), }) } - /// 初始化模块状态 - pub fn initialize_module(&self) -> Result<()> { + pub fn initialize_module(&self) -> Result { info!("开始模块初始化..."); - // 确保free文件存在 if !Path::new(FREE_FILE).exists() { info!("free文件不存在,创建并设置为1"); FileMonitor::write_file_content(FREE_FILE, "1")?; } - // 确保disable文件不存在(模块启用状态) if Path::new(DISABLE_FILE).exists() { info!("检测到disable文件,删除以启用模块"); fs::remove_file(DISABLE_FILE).map_err(FreePPSError::FileOperation)?; } - // 读取当前free文件状态并主动更新描述 - let free_content = FileMonitor::read_file_content(FREE_FILE)?; - info!("当前free文件内容: {}", free_content); - - if free_content == "1" { - info!("模块启用 - 锁定PPS支持模式"); - #[cfg(unix)] - self.update_module_description(true)?; - - if Path::new(PD_VERIFIED_PATH).exists() { - info!("初始化:设置qcom节点为1"); - match PdVerifier::new() { - Ok(pd_verifier) => match pd_verifier.set_pd_verified(true) { - Ok(_) => info!("qcom节点初始化成功"), - Err(e) => warn!("设置qcom节点失败: {}", e), - }, - Err(e) => warn!("创建PD验证器失败: {}", e), - } - } - - #[cfg(unix)] - { - if Path::new(PD_ADAPTER_VERIFIED_PATH).exists() { - info!("初始化:设置mtk节点为1"); - match PdAdapterVerifier::new() { - Ok(pd_adapter_verifier) => { - match pd_adapter_verifier.set_pd_adapter_verified(true) { - Ok(_) => info!("mtk节点初始化成功"), - Err(e) => warn!("设置mtk节点失败: {}", e), - } - } - Err(e) => warn!("创建PD适配器验证器失败: {}", e), - } - } - } - } else { - info!("模块已暂停(free=0)"); - #[cfg(unix)] - self.update_module_description(false)?; - #[cfg(unix)] - self.restore_pd_when_idle(); - } - - info!("模块初始化完成"); - Ok(()) + let mode = ChargingMode::from_files()?; + self.apply_mode(mode)?; + *self.last_mode.lock().unwrap() = Some(mode); + info!("模块初始化完成,当前模式: {:?}", mode); + Ok(mode) } - /// 更新module.prop描述 #[cfg(unix)] - pub fn update_module_description(&self, enabled: bool) -> Result<()> { + fn update_module_description(&self, mode: ChargingMode) -> Result<()> { let prop_content = FileMonitor::read_file_content(MODULE_PROP)?; - - let status_prefix = if !enabled { - "[⏸️PPS已暂停💤] " - } else { - "[✅锁定PPS支持⚡] " - }; + const PREFIXES: [&str; 4] = [ + "[✅锁定PPS支持⚡] ", + "[⏸️PPS已暂停💤] ", + "[⏸️小米协议优先💤] ", + "[🔄协议自动识别💡] ", + ]; let updated_content = prop_content .lines() .map(|line| { - if line.starts_with("description=") { - let original_description = line.strip_prefix("description=").unwrap_or(""); - let clean_description = - if original_description.starts_with("[✅锁定PPS支持⚡] ") { - original_description - .strip_prefix("[✅锁定PPS支持⚡] ") - .unwrap_or(original_description) - } else if original_description.starts_with("[⏸️PPS已暂停💤] ") { - original_description - .strip_prefix("[⏸️PPS已暂停💤] ") - .unwrap_or(original_description) - } else { - original_description - }; - format!("description={}{}", status_prefix, clean_description) + if let Some(description) = line.strip_prefix("description=") { + let clean = PREFIXES + .iter() + .find_map(|prefix| description.strip_prefix(prefix)) + .unwrap_or(description); + format!("description={}{}", mode.description_prefix(), clean) } else { line.to_string() } @@ -129,123 +70,79 @@ impl ModuleManager { .join("\n"); FileMonitor::write_file_content(MODULE_PROP, &updated_content)?; - info!( - "更新module.prop描述,添加状态前缀: {}", - status_prefix.trim() - ); Ok(()) } - /// free=0 且未插电时,将pd节点还原为0,确保下次插电前为0、不误解锁高功率PPS; - /// 已插电(充电中)则不动pd,让内核/MIPPS自然握手,避免写入0干扰协议协商 - #[cfg(unix)] - fn restore_pd_when_idle(&self) { - let battery_status = - FileMonitor::read_file_content(BATTERY_STATUS_PATH).unwrap_or_default(); - if battery_status != "Discharging" { - info!( - "当前电池状态={},free=0不动pd,交由内核/MIPPS自然握手", - battery_status - ); - return; - } + fn apply_mode(&self, mode: ChargingMode) -> Result<()> { + info!("应用充电协议模式: {:?}", mode); + #[cfg(unix)] + self.update_module_description(mode)?; if Path::new(PD_VERIFIED_PATH).exists() { - match PdVerifier::new() { - Ok(pd_verifier) => match pd_verifier.set_pd_verified(false) { - Ok(_) => {} - Err(e) => warn!("设置PD验证状态失败: {},跳过此步骤", e), - }, - Err(e) => warn!("创建PD验证器失败: {},跳过此步骤", e), + let public_pps = mode == ChargingMode::ForcePublicPps; + let should_write = public_pps || self.is_idle(); + if should_write { + match PdVerifier::new().and_then(|verifier| verifier.set_pd_verified(public_pps)) { + Ok(()) => info!("qcom pd_verifed初始化为{}", u8::from(public_pps)), + Err(error) => warn!("设置qcom节点失败: {}", error), + } + } else { + info!("设备已连接,切换到 {:?} 时不强制清零qcom节点", mode); } - } else { - warn!("PD验证文件不存在,跳过恢复"); } + #[cfg(unix)] if Path::new(PD_ADAPTER_VERIFIED_PATH).exists() { - match PdAdapterVerifier::new() { - Ok(pd_adapter_verifier) => { - match pd_adapter_verifier.set_pd_adapter_verified(false) { - Ok(_) => {} - Err(e) => warn!("设置PD适配器验证状态失败: {},跳过此步骤", e), - } + // Automatic detection has only been verified on QCOM. On MTK, keep + // automatic mode equivalent to public PPS rather than disabling it. + let public_pps = mode != ChargingMode::Native; + let should_write = public_pps || self.is_idle(); + if should_write { + match PdAdapterVerifier::new() + .and_then(|verifier| verifier.set_pd_adapter_verified(public_pps)) + { + Ok(()) => info!( + "mtk pd_adapter/usbpd_verifed初始化为{}", + u8::from(public_pps) + ), + Err(error) => warn!("设置mtk节点失败: {}", error), } - Err(e) => warn!("创建PD适配器验证器失败: {},跳过此步骤", e), + } else { + info!("设备已连接,切换到原生模式时不强制清零mtk节点"); } - } else { - warn!("PD适配器验证文件不存在,跳过恢复"); } + + Ok(()) } - /// 处理free文件变化 #[cfg(unix)] - pub fn handle_free_file_change(&self, content: &str) -> Result<()> { - let current_state = content.to_string(); - - // 获取上次状态并检查是否相同 - { - let mut last_state = self.last_state.lock().unwrap(); - if *last_state == current_state { - // 状态未变化,跳过处理 - return Ok(()); - } - // 更新状态缓存 - *last_state = current_state; - } + fn is_idle(&self) -> bool { + FileMonitor::read_file_content(BATTERY_STATUS_PATH).unwrap_or_default() == "Discharging" + } - info!("free文件内容: {}", content); - - if content == "1" { - info!("free文件为1,启用锁定PPS支持模式"); - self.update_module_description(true)?; - - // free=1 时设置pd_verifed=1(与initialize_module一致),解锁高功率PPS; - // 否则free置1后pd保持旧值,下次插电可能无法解锁 - if Path::new(PD_VERIFIED_PATH).exists() { - match PdVerifier::new() { - Ok(pd_verifier) => match pd_verifier.set_pd_verified(true) { - Ok(_) => {} - Err(e) => warn!("设置PD验证状态失败: {},跳过此步骤", e), - }, - Err(e) => warn!("创建PD验证器失败: {},跳过此步骤", e), - } - } - #[cfg(unix)] - { - if Path::new(PD_ADAPTER_VERIFIED_PATH).exists() { - match PdAdapterVerifier::new() { - Ok(pd_adapter_verifier) => { - match pd_adapter_verifier.set_pd_adapter_verified(true) { - Ok(_) => {} - Err(e) => warn!("设置PD适配器验证状态失败: {},跳过此步骤", e), - } - } - Err(e) => warn!("创建PD适配器验证器失败: {},跳过此步骤", e), - } - } - } - } else if content == "0" { - info!("free文件为0,暂停模块"); - self.update_module_description(false)?; - // free=0 时:未插电还原pd为0,已插电不动pd(交由内核/MIPPS自然握手) - self.restore_pd_when_idle(); + #[cfg(not(unix))] + fn is_idle(&self) -> bool { + true + } + + pub fn handle_configuration_change(&self) -> Result { + let mode = ChargingMode::from_files()?; + let mut last_mode = self.last_mode.lock().unwrap(); + if *last_mode != Some(mode) { + self.apply_mode(mode)?; + *last_mode = Some(mode); } - Ok(()) + Ok(mode) } - /// 处理disable文件变化 #[cfg(unix)] pub fn handle_disable_file_change(&self, exists: bool) -> Result<()> { if exists { info!("检测到disable文件创建"); - // disable文件出现,设置free为0 FileMonitor::write_file_content(FREE_FILE, "0")?; - info!("已处理disable文件创建事件"); } else { info!("检测到disable文件删除"); - // disable文件消失,设置free为1 FileMonitor::write_file_content(FREE_FILE, "1")?; - info!("已处理disable文件删除事件"); } Ok(()) } diff --git a/src/monitoring/threads/disable_file.rs b/src/monitoring/threads/disable_file.rs index 290bc2a..a8fa83e 100644 --- a/src/monitoring/threads/disable_file.rs +++ b/src/monitoring/threads/disable_file.rs @@ -11,6 +11,7 @@ use crate::common::utils; #[cfg(unix)] use crate::monitoring::FileMonitor; use crate::monitoring::ModuleManager; +use crate::platform::EventFd; #[cfg(unix)] use std::io; #[cfg(unix)] @@ -19,30 +20,35 @@ use std::path::Path; pub fn spawn_disable_file_monitor( running: Arc, module_manager: Arc, + stop_event: Arc, ) -> thread::JoinHandle<()> { thread::Builder::new() .name("disable-file-monitor".to_string()) .spawn(move || { - if let Err(e) = worker(running, module_manager) { + if let Err(e) = worker(running, module_manager, stop_event) { error!("disable文件监控线程出错: {}", e); } }) .expect("创建disable文件监控线程失败") } -fn worker(running: Arc, module_manager: Arc) -> Result<()> { +fn worker( + running: Arc, + module_manager: Arc, + stop_event: Arc, +) -> Result<()> { let thread_name = utils::get_current_thread_name(); info!("[{}] 启动disable文件监控线程...", thread_name); #[cfg(unix)] { let mut disable_exists = Path::new(DISABLE_FILE).exists(); - run_unix(running, module_manager, &mut disable_exists)?; + run_unix(running, module_manager, &mut disable_exists, stop_event)?; } #[cfg(not(unix))] { - let _ = (running, module_manager); + let _ = (running, module_manager, stop_event); } Ok(()) @@ -53,12 +59,18 @@ fn run_unix( running: Arc, module_manager: Arc, disable_exists: &mut bool, + stop_event: Arc, ) -> Result<()> { let file_monitor = FileMonitor::new()?; file_monitor.add_watch(MODULE_BASE_PATH, IN_CREATE | IN_DELETE)?; // 将 inotify_fd 添加到 epoll file_monitor.add_inotify_to_epoll()?; + file_monitor.add_fd_to_epoll( + stop_event.raw_fd(), + libc::EPOLLIN as u32, + stop_event.raw_fd() as u64, + )?; let mut buffer = [0u8; 1024]; let mut events = [libc::epoll_event { events: 0, u64: 0 }; 8]; @@ -79,6 +91,21 @@ fn run_unix( continue; } + let ready_events = &events[..nfds as usize]; + if ready_events + .iter() + .any(|event| event.u64 == stop_event.raw_fd() as u64) + { + stop_event.clear()?; + break; + } + if !ready_events + .iter() + .any(|event| event.u64 == file_monitor.inotify_fd as u64) + { + continue; + } + let bytes_read = unsafe { let count = buffer.len(); libc::read( diff --git a/src/monitoring/threads/free_file.rs b/src/monitoring/threads/free_file.rs index 5069aa0..26c1b9d 100644 --- a/src/monitoring/threads/free_file.rs +++ b/src/monitoring/threads/free_file.rs @@ -1,7 +1,6 @@ use std::path::Path; use std::sync::Arc; -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use std::thread; use anyhow::Result; @@ -9,54 +8,76 @@ use log::{error, info}; use crate::common::constants::FREE_FILE; #[cfg(unix)] -use crate::common::constants::IN_CLOSE_WRITE; -#[cfg(unix)] -use crate::common::constants::IN_MODIFY; +use crate::common::constants::{IN_CLOSE_WRITE, IN_CREATE, IN_DELETE, IN_MODIFY, MODULE_BASE_PATH}; use crate::common::utils; -use crate::monitoring::{FileMonitor, ModuleManager}; +use crate::monitoring::{ChargingMode, FileMonitor, ModuleManager}; +use crate::platform::EventFd; #[cfg(unix)] use std::io; pub fn spawn_free_file_monitor( running: Arc, module_manager: Arc, - free_enabled: Arc, + charging_mode: Arc, + qcom_config_event: Arc, + mtk_config_event: Arc, + stop_event: Arc, ) -> thread::JoinHandle<()> { thread::Builder::new() - .name("free-file-monitor".to_string()) + .name("mode-file-monitor".to_string()) .spawn(move || { - if let Err(e) = worker(running, module_manager, free_enabled) { - error!("free文件监控线程出错: {}", e); + if let Err(error) = worker( + running, + module_manager, + charging_mode, + qcom_config_event, + mtk_config_event, + stop_event, + ) { + error!("模式文件监控线程出错: {}", error); } }) - .expect("创建free文件监控线程失败") + .expect("创建模式文件监控线程失败") } fn worker( running: Arc, module_manager: Arc, - free_enabled: Arc, + charging_mode: Arc, + qcom_config_event: Arc, + mtk_config_event: Arc, + stop_event: Arc, ) -> Result<()> { - let thread_name = utils::get_current_thread_name(); - info!("[{}] 启动free文件监控线程...", thread_name); - + info!( + "[{}] 启动模式文件监控线程...", + utils::get_current_thread_name() + ); if !Path::new(FREE_FILE).exists() { FileMonitor::write_file_content(FREE_FILE, "1")?; } - let initial = - FileMonitor::read_file_content(FREE_FILE).unwrap_or_else(|_| "0".to_string()) == "1"; - free_enabled.store(initial, Ordering::Relaxed); + let initial = ChargingMode::from_files()?; + charging_mode.store(initial as u8, Ordering::Relaxed); #[cfg(unix)] - { - run_unix(running, module_manager, free_enabled)?; - } + run_unix( + running, + module_manager, + charging_mode, + qcom_config_event, + mtk_config_event, + stop_event, + )?; #[cfg(not(unix))] - { - let _ = (running, module_manager, free_enabled); - } + let _ = ( + running, + module_manager, + charging_mode, + qcom_config_event, + mtk_config_event, + stop_event, + ); Ok(()) } @@ -65,126 +86,91 @@ fn worker( fn run_unix( running: Arc, module_manager: Arc, - free_enabled: Arc, + charging_mode: Arc, + qcom_config_event: Arc, + mtk_config_event: Arc, + stop_event: Arc, ) -> Result<()> { let file_monitor = FileMonitor::new()?; - - // 先添加所有需要监控的路径 - file_monitor.add_watch(FREE_FILE, IN_MODIFY | IN_CLOSE_WRITE)?; - + let free_watch = file_monitor.add_watch(FREE_FILE, IN_MODIFY | IN_CLOSE_WRITE)?; + let directory_watch = file_monitor.add_watch(MODULE_BASE_PATH, IN_CREATE | IN_DELETE)?; file_monitor.add_inotify_to_epoll()?; + file_monitor.add_fd_to_epoll( + stop_event.raw_fd(), + libc::EPOLLIN as u32, + stop_event.raw_fd() as u64, + )?; - let mut buffer = [0u8; 1024]; + let mut buffer = [0u8; 2048]; let mut events = [libc::epoll_event { events: 0, u64: 0 }; 8]; - while running.load(std::sync::atomic::Ordering::Relaxed) { + while running.load(Ordering::Relaxed) { let nfds = match file_monitor.wait_events(&mut events, -1) { - Ok(nfds) => nfds, - Err(err) => match err.raw_os_error() { - Some(code) if code == libc::EINTR || code == libc::EAGAIN => continue, - _ => { - error!("等待inotify事件失败,将在1秒后重试:{}", err); - thread::sleep(std::time::Duration::from_millis(1000)); - continue; - } - }, + Ok(count) => count, + Err(error) if matches!(error.raw_os_error(), Some(code) if code == libc::EINTR || code == libc::EAGAIN) => + { + continue; + } + Err(error) => return Err(error.into()), }; - if nfds <= 0 { + let ready = &events[..nfds as usize]; + if ready + .iter() + .any(|event| event.u64 == stop_event.raw_fd() as u64) + { + stop_event.clear()?; + break; + } + if !ready + .iter() + .any(|event| event.u64 == file_monitor.inotify_fd as u64) + { continue; } let bytes_read = unsafe { - let count = buffer.len(); libc::read( file_monitor.inotify_fd, - buffer.as_mut_ptr() as *mut std::os::raw::c_void, - count, + buffer.as_mut_ptr().cast::(), + buffer.len(), ) }; - if bytes_read == -1 { - let err = io::Error::last_os_error(); - match err.raw_os_error() { - Some(code) if code == libc::EINTR || code == libc::EAGAIN => continue, - _ => { - error!("读取inotify事件失败({}),1秒后重试", err); - thread::sleep(std::time::Duration::from_millis(1000)); - continue; - } + let error = io::Error::last_os_error(); + if matches!(error.raw_os_error(), Some(code) if code == libc::EINTR || code == libc::EAGAIN) + { + continue; } - } else if bytes_read > 0 { - let bytes_read = bytes_read as usize; - let event_size = std::mem::size_of::(); - let mut offset = 0usize; - let mut should_process_free = false; - - while offset + event_size <= bytes_read { - let event_ptr = - unsafe { buffer.as_ptr().add(offset) as *const libc::inotify_event }; - let event = unsafe { &*event_ptr }; + return Err(error.into()); + } - // 检查是否是free文件的修改事件 - if (event.mask & libc::IN_CLOSE_WRITE) != 0 { - should_process_free = true; + let mut offset = 0usize; + let mut changed = false; + let event_size = std::mem::size_of::(); + while offset + event_size <= bytes_read as usize { + let event = unsafe { &*(buffer.as_ptr().add(offset) as *const libc::inotify_event) }; + if event.wd == free_watch && event.mask & (IN_MODIFY | IN_CLOSE_WRITE) != 0 { + changed = true; + } else if event.wd == directory_watch && event.len > 0 { + let name_start = offset + event_size; + let name_bytes = &buffer[name_start..name_start + event.len as usize]; + let end = name_bytes + .iter() + .position(|byte| *byte == 0) + .unwrap_or(name_bytes.len()); + if &name_bytes[..end] == b"auto" && event.mask & (IN_CREATE | IN_DELETE) != 0 { + changed = true; } - - let name_len = event.len as usize; - offset += event_size + name_len; } + offset += event_size + event.len as usize; + } - if should_process_free { - info!("检测到free文件变化"); - - // 延迟100ms以便: - // 1. 让文件系统操作完全完成 - // 2. 让后续相关事件也进入inotify队列 - thread::sleep(std::time::Duration::from_millis(100)); - - // 将 inotify_fd 临时设置为非阻塞模式 - let flags = unsafe { libc::fcntl(file_monitor.inotify_fd, libc::F_GETFL, 0) }; - if flags != -1 { - unsafe { - libc::fcntl( - file_monitor.inotify_fd, - libc::F_SETFL, - flags | libc::O_NONBLOCK, - ); - } - - // 排空所有待处理的事件 - let mut temp_buffer = [0u8; 1024]; - loop { - let bytes = unsafe { - libc::read( - file_monitor.inotify_fd, - temp_buffer.as_mut_ptr() as *mut std::os::raw::c_void, - temp_buffer.len(), - ) - }; - - if bytes <= 0 { - break; - } - } - - // 恢复为阻塞模式 - unsafe { - libc::fcntl( - file_monitor.inotify_fd, - libc::F_SETFL, - flags & !libc::O_NONBLOCK, - ); - } - } - - // 读取 free 文件内容 - let content = FileMonitor::read_file_content(FREE_FILE)?; - let enabled = content == "1"; - free_enabled.store(enabled, Ordering::Relaxed); - - // 更新模块描述 - module_manager.handle_free_file_change(&content)?; - } + if changed { + let mode = module_manager.handle_configuration_change()?; + charging_mode.store(mode as u8, Ordering::Release); + qcom_config_event.notify()?; + mtk_config_event.notify()?; + info!("模式文件变化,切换为 {:?}", mode); } } diff --git a/src/monitoring/threads/pd_adapter_verified.rs b/src/monitoring/threads/pd_adapter_verified.rs index 8be99c8..f25f582 100644 --- a/src/monitoring/threads/pd_adapter_verified.rs +++ b/src/monitoring/threads/pd_adapter_verified.rs @@ -1,27 +1,41 @@ use std::sync::Arc; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use std::thread; use anyhow::Result; use log::{debug, error, info}; #[cfg(unix)] -use crate::common::constants::{FREE_FILE, IN_CLOSE_WRITE, IN_MODIFY, PD_ADAPTER_VERIFIED_PATH}; +use crate::common::FreePPSError; +#[cfg(unix)] +use crate::common::constants::PD_ADAPTER_VERIFIED_PATH; use crate::common::utils; +use crate::monitoring::ChargingMode; #[cfg(unix)] use crate::monitoring::FileMonitor; use crate::pd::PdAdapterVerifier; +use crate::platform::EventFd; +#[cfg(unix)] +use std::io; pub fn spawn_pd_adapter_verified_monitor( running: Arc, pd_adapter_verifier: Arc, - free_enabled: Arc, + charging_mode: Arc, + config_event: Arc, + stop_event: Arc, ) -> thread::JoinHandle<()> { thread::Builder::new() .name("mtk".to_string()) .spawn(move || { - if let Err(e) = worker(running, pd_adapter_verifier, free_enabled) { - error!("mtk线程出错: {}", e); + if let Err(error) = worker( + running, + pd_adapter_verifier, + charging_mode, + config_event, + stop_event, + ) { + error!("mtk线程出错: {}", error); } }) .expect("创建mtk线程失败") @@ -30,19 +44,50 @@ pub fn spawn_pd_adapter_verified_monitor( fn worker( running: Arc, pd_adapter_verifier: Arc, - free_enabled: Arc, + charging_mode: Arc, + config_event: Arc, + stop_event: Arc, ) -> Result<()> { - let thread_name = utils::get_current_thread_name(); - info!("[{}] 启动mtk监控线程...", thread_name); + info!("[{}] 启动mtk监控线程...", utils::get_current_thread_name()); #[cfg(unix)] - run_unix(running, pd_adapter_verifier, free_enabled)?; + run_unix( + running, + pd_adapter_verifier, + charging_mode, + config_event, + stop_event, + )?; #[cfg(not(unix))] - { - let _ = (running, pd_adapter_verifier, free_enabled); + let _ = ( + running, + pd_adapter_verifier, + charging_mode, + config_event, + stop_event, + ); + + Ok(()) +} + +#[cfg(unix)] +fn add_epoll_fd(epoll_fd: libc::c_int, fd: libc::c_int, events: u32) -> Result<()> { + let mut event = libc::epoll_event { + events, + u64: fd as u64, + }; + if unsafe { libc::epoll_ctl(epoll_fd, libc::EPOLL_CTL_ADD, fd, &mut event) } == -1 { + return Err(io::Error::last_os_error().into()); } + Ok(()) +} +#[cfg(unix)] +fn remove_epoll_fd(epoll_fd: libc::c_int, fd: libc::c_int) -> Result<()> { + if unsafe { libc::epoll_ctl(epoll_fd, libc::EPOLL_CTL_DEL, fd, std::ptr::null_mut()) } == -1 { + return Err(io::Error::last_os_error().into()); + } Ok(()) } @@ -50,222 +95,136 @@ fn worker( fn run_unix( running: Arc, pd_adapter_verifier: Arc, - free_enabled: Arc, + charging_mode: Arc, + config_event: Arc, + stop_event: Arc, ) -> Result<()> { - use std::sync::atomic::Ordering; - - // 每线程独立创建 inotify(监控 free 文件),与 uevent 共用同一 epoll: - // free=0 时也无限阻塞在 epoll_wait,由 free 文件 inotify 事件唤醒,实现零周期唤醒 - let file_monitor = FileMonitor::new()?; - file_monitor.add_watch(FREE_FILE, IN_MODIFY | IN_CLOSE_WRITE)?; - file_monitor.add_inotify_to_epoll()?; - let uevent_sock = FileMonitor::create_uevent_monitor()?; - if let Err(e) = file_monitor.add_fd_to_epoll( - uevent_sock, - (libc::EPOLLIN | libc::EPOLLPRI) as u32, - uevent_sock as u64, - ) { + let epoll_fd = unsafe { libc::epoll_create1(libc::EPOLL_CLOEXEC) }; + if epoll_fd == -1 { + unsafe { libc::close(uevent_sock) }; + return Err(FreePPSError::InotifyError("无法初始化epoll".to_string()).into()); + } + + let mode = ChargingMode::from_raw(charging_mode.load(Ordering::Acquire)); + let mut uevent_registered = mode != ChargingMode::Native; + let setup = add_epoll_fd(epoll_fd, stop_event.raw_fd(), libc::EPOLLIN as u32) + .and_then(|()| add_epoll_fd(epoll_fd, config_event.raw_fd(), libc::EPOLLIN as u32)) + .and_then(|()| { + if uevent_registered { + add_epoll_fd( + epoll_fd, + uevent_sock, + (libc::EPOLLIN | libc::EPOLLPRI) as u32, + ) + } else { + Ok(()) + } + }); + if let Err(error) = setup { unsafe { libc::close(uevent_sock); + libc::close(epoll_fd); } - return Err(e); - } - - info!( - "[{}] 开始通过uevent监控mtk状态: {}", - utils::get_current_thread_name(), - PD_ADAPTER_VERIFIED_PATH - ); - - // free 暂停状态用本线程本地变量维护:仅初始化时读取共享原子, - // 之后由 inotify 唤醒时直接读 free 文件作为权威状态。 - // (free_file.rs 更新共享原子前有约100ms延迟,若依赖原子可能漏掉 0↔1 切换) - let mut enabled = free_enabled.load(Ordering::Relaxed); - if !enabled { - // 启动即暂停:从 epoll 移除 uevent socket,暂停期间仅由 free 文件 inotify 唤醒 - file_monitor.remove_fd_from_epoll(uevent_sock)?; + return Err(error); } - let mut eintr_count: u64 = 0; - let mut eagain_count: u64 = 0; + info!("通过配置事件与uevent监控mtk状态"); let mut charging_session_active = false; - let mut last_interrupt_report = std::time::Instant::now(); - let interrupt_report_interval = std::time::Duration::from_secs(60 * 60 * 10); - - let mut events = [libc::epoll_event { events: 0, u64: 0 }; 10]; + let mut events = [libc::epoll_event { events: 0, u64: 0 }; 8]; while running.load(Ordering::Relaxed) { - let nfds = match file_monitor.wait_events(&mut events, -1) { - Ok(nfds) => nfds, - Err(err) => { - match err.raw_os_error() { - Some(code) if code == libc::EINTR || code == libc::EAGAIN => { - if code == libc::EINTR { - eintr_count += 1; - } else { - eagain_count += 1; - } - - let now = std::time::Instant::now(); - if now.duration_since(last_interrupt_report) >= interrupt_report_interval - && (eintr_count > 0 || eagain_count > 0) - { - debug!( - "epoll_wait暂时中断统计(最近{}秒): EINTR={}次, EAGAIN={}次", - interrupt_report_interval.as_secs(), - eintr_count, - eagain_count - ); - eintr_count = 0; - eagain_count = 0; - last_interrupt_report = now; - } - } - Some(code) => { - error!("epoll_wait错误(code={}),5秒后重试:{}", code, err); - std::thread::sleep(std::time::Duration::from_millis(5000)); - } - None => { - error!("epoll_wait错误(未知code),5秒后重试:{}", err); - std::thread::sleep(std::time::Duration::from_millis(5000)); - } - } + let nfds = unsafe { + libc::epoll_wait( + epoll_fd, + events.as_mut_ptr(), + events.len() as libc::c_int, + -1, + ) + }; + if nfds == -1 { + let error = io::Error::last_os_error(); + if matches!(error.raw_os_error(), Some(code) if code == libc::EINTR || code == libc::EAGAIN) + { continue; } - }; - - if nfds <= 0 { + error!("mtk epoll_wait失败: {}", error); + thread::sleep(std::time::Duration::from_secs(1)); continue; } - // 优先处理 free 文件 inotify 事件,刷新 enabled 后再处理 uevent, - // 保证同一批事件中 free=0 时 uevent 不会被误处理 - if events + let ready = &events[..nfds as usize]; + if ready .iter() - .take(nfds as usize) - .any(|ev| ev.u64 == file_monitor.inotify_fd as u64) + .any(|event| event.u64 == stop_event.raw_fd() as u64) { - let mut inotify_buffer = [0u8; 1024]; - let bytes_read = unsafe { - libc::read( - file_monitor.inotify_fd, - inotify_buffer.as_mut_ptr() as *mut std::os::raw::c_void, - inotify_buffer.len(), - ) - }; - - if bytes_read > 0 { - let bytes_read = bytes_read as usize; - let event_size = std::mem::size_of::(); - let mut offset = 0usize; - let mut close_write_seen = false; - while offset + event_size <= bytes_read { - let event_ptr = unsafe { - inotify_buffer.as_ptr().add(offset) as *const libc::inotify_event - }; - let event = unsafe { &*event_ptr }; - if (event.mask & libc::IN_CLOSE_WRITE) != 0 { - close_write_seen = true; - } - offset += event_size + event.len as usize; - } + stop_event.clear()?; + break; + } - if close_write_seen { - // 直接读 free 文件内容作为权威状态 - let new_enabled = FileMonitor::read_file_content(FREE_FILE)? == "1"; - if new_enabled != enabled { - if new_enabled { - // 恢复:把 uevent socket 重新加入 epoll - file_monitor.add_fd_to_epoll( - uevent_sock, - (libc::EPOLLIN | libc::EPOLLPRI) as u32, - uevent_sock as u64, - )?; - info!("[mtk] free文件恢复为1,重新启动PD适配器验证节点监控"); - } else { - // 暂停:从 epoll 移除 uevent socket,暂停期间不再被 uevent 唤醒 - file_monitor.remove_fd_from_epoll(uevent_sock)?; - info!("[mtk] free文件为0,暂停PD适配器验证节点监控"); - } - enabled = new_enabled; - } + if ready + .iter() + .any(|event| event.u64 == config_event.raw_fd() as u64) + { + config_event.clear()?; + let enabled = ChargingMode::from_raw(charging_mode.load(Ordering::Acquire)) + != ChargingMode::Native; + if enabled != uevent_registered { + if enabled { + add_epoll_fd( + epoll_fd, + uevent_sock, + (libc::EPOLLIN | libc::EPOLLPRI) as u32, + )?; + } else { + remove_epoll_fd(epoll_fd, uevent_sock)?; + charging_session_active = false; } + uevent_registered = enabled; + info!("[mtk] uevent监控状态: {}", enabled); } } - // free=0 时 uevent socket 已从 epoll 移除,不会因 uevent 被唤醒; - // 同一批事件中残留的 uevent 事件在暂停态直接跳过,不读取 - for ev in events.iter().take(nfds as usize) { - if ev.u64 != uevent_sock as u64 || !enabled { - continue; - } + if !uevent_registered || !ready.iter().any(|event| event.u64 == uevent_sock as u64) { + continue; + } - let mut buffer = [0u8; 4096]; + let mut buffer = [0u8; 4096]; + loop { let bytes_read = unsafe { libc::recv( uevent_sock, - buffer.as_mut_ptr() as *mut std::os::raw::c_void, + buffer.as_mut_ptr().cast::(), buffer.len(), libc::MSG_DONTWAIT, ) }; - if bytes_read <= 0 { - continue; - } - - let uevent_data = String::from_utf8_lossy(&buffer[..bytes_read as usize]); - - // 检查是否为POWER_SUPPLY事件 - let is_power_supply_event = uevent_data.contains("POWER_SUPPLY"); - - // 提取POWER_SUPPLY_STATUS - let fields = uevent_data.split(['\0', '\n']); - let status = fields - .clone() - .find(|field| field.starts_with("POWER_SUPPLY_STATUS=")) - .and_then(|field| field.split_once('=').map(|(_, value)| value)); - - let mut should_set_node = false; - - if is_power_supply_event { - debug!("[mtk] 锁定PPS模式:检测到POWER_SUPPLY事件"); - should_set_node = true; + break; } + let data = String::from_utf8_lossy(&buffer[..bytes_read as usize]); + let status = data + .split(['\0', '\n']) + .find_map(|field| field.strip_prefix("POWER_SUPPLY_STATUS=")); + let mut should_set_node = data.contains("POWER_SUPPLY"); if let Some("Discharging") = status { - if charging_session_active { - info!("[mtk] 锁定PPS模式:检测到Charging→Discharging状态跳变"); - should_set_node = true; - charging_session_active = false; - } - } else if let Some("Charging") = status - && !charging_session_active - { + should_set_node |= charging_session_active; + charging_session_active = false; + } else if let Some("Charging") = status { charging_session_active = true; } - if should_set_node { - let pd_adapter_content = FileMonitor::read_file_content(PD_ADAPTER_VERIFIED_PATH)?; - if pd_adapter_content == "0" { - info!("[mtk] 锁定PPS模式:设置节点为1"); - pd_adapter_verifier.set_pd_adapter_verified(true)?; - } + if should_set_node && FileMonitor::read_file_content(PD_ADAPTER_VERIFIED_PATH)? == "0" { + debug!("[mtk] 设置pd_adapter验证节点为1"); + pd_adapter_verifier.set_pd_adapter_verified(true)?; } } } - if eintr_count > 0 || eagain_count > 0 { - debug!( - "epoll_wait暂时中断统计(线程退出前): EINTR={}次, EAGAIN={}次", - eintr_count, eagain_count - ); - } - unsafe { libc::close(uevent_sock); + libc::close(epoll_fd); } - Ok(()) } diff --git a/src/monitoring/threads/pd_verified.rs b/src/monitoring/threads/pd_verified.rs index 889da91..d62baf1 100644 --- a/src/monitoring/threads/pd_verified.rs +++ b/src/monitoring/threads/pd_verified.rs @@ -1,36 +1,67 @@ use std::sync::Arc; -use std::sync::atomic::AtomicBool; -#[cfg(unix)] -use std::sync::atomic::AtomicU32; +use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, Ordering}; use std::thread; +use std::time::{Duration, Instant}; use anyhow::Result; -use log::debug; -use log::{error, info}; +use log::{debug, error, info, warn}; #[cfg(unix)] use crate::common::constants::{ - BATTERY_STATUS_PATH, FREE_FILE, IN_CLOSE_WRITE, IN_MODIFY, PD_VERIFIED_PATH, + BATTERY_STATUS_PATH, INPUT_SUSPEND_PATH, PD_VERIFIED_PATH, TYPEC_MODE_PATH, USB_REAL_TYPE_PATH, }; use crate::common::utils; +use crate::monitoring::ChargingMode; #[cfg(unix)] use crate::monitoring::FileMonitor; -use crate::pd::PdVerifier; -#[cfg(unix)] -use crate::pd::{BroadcastForger, spawn_broadcast_forger_worker}; -#[cfg(unix)] -use std::sync::atomic::Ordering; +use crate::pd::{BroadcastForger, PdVerifier, spawn_broadcast_forger_worker}; +use crate::platform::EventFd; + +const NATIVE_NEGOTIATION_TIMEOUT: Duration = Duration::from_secs(4); +const RECONNECT_STEP_DELAY: Duration = Duration::from_secs(1); +const DETACH_DEBOUNCE: Duration = Duration::from_millis(1500); +// Qualcomm uevents can be delivered just before the matching sysfs values are +// visible. Reconcile once more after the event instead of polling while idle. +const UEVENT_SETTLE_DELAY: Duration = Duration::from_millis(250); + +#[derive(Clone, Copy, Debug)] +enum AutoPhase { + Idle, + WaitingNative(Instant), + Suspended(Instant), + PublicEnabled(Instant), + Settled, +} + +impl AutoPhase { + fn deadline(self) -> Option { + match self { + Self::WaitingNative(deadline) + | Self::Suspended(deadline) + | Self::PublicEnabled(deadline) => Some(deadline), + Self::Idle | Self::Settled => None, + } + } +} pub fn spawn_pd_verified_monitor( running: Arc, pd_verifier: Arc, - free_enabled: Arc, + charging_mode: Arc, + config_event: Arc, + stop_event: Arc, ) -> thread::JoinHandle<()> { thread::Builder::new() .name("qcom".to_string()) .spawn(move || { - if let Err(e) = worker(running, pd_verifier, free_enabled) { - error!("qcom线程出错: {}", e); + if let Err(error) = worker( + running, + pd_verifier, + charging_mode, + config_event, + stop_event, + ) { + error!("qcom线程出错: {}", error); } }) .expect("创建qcom线程失败") @@ -39,292 +70,417 @@ pub fn spawn_pd_verified_monitor( fn worker( running: Arc, pd_verifier: Arc, - free_enabled: Arc, + charging_mode: Arc, + config_event: Arc, + stop_event: Arc, ) -> Result<()> { - let thread_name = utils::get_current_thread_name(); - info!("[{}] 启动qcom监控线程...", thread_name); + info!("[{}] 启动qcom监控线程...", utils::get_current_thread_name()); #[cfg(unix)] - run_unix(running, pd_verifier, free_enabled)?; + run_unix( + running, + pd_verifier, + charging_mode, + config_event, + stop_event, + )?; #[cfg(not(unix))] - { - let _ = (running, pd_verifier, free_enabled); - } + let _ = ( + running, + pd_verifier, + charging_mode, + config_event, + stop_event, + ); Ok(()) } +#[cfg(unix)] +fn is_attached() -> Result { + Ok(FileMonitor::read_file_content(TYPEC_MODE_PATH)? != "Nothing attached") +} + +#[cfg(unix)] +fn set_input_suspended(suspended: bool) -> Result<()> { + FileMonitor::write_file_content(INPUT_SUSPEND_PATH, if suspended { "1" } else { "0" }) +} + +#[cfg(unix)] +fn epoll_timeout( + phase: AutoPhase, + detach_deadline: Option, + uevent_recheck_deadline: Option, + charger_detach_deadline: Option, +) -> libc::c_int { + let deadline = [ + phase.deadline(), + detach_deadline, + uevent_recheck_deadline, + charger_detach_deadline, + ] + .into_iter() + .flatten() + .min(); + let Some(deadline) = deadline else { + return -1; + }; + let remaining = deadline.saturating_duration_since(Instant::now()); + if remaining.is_zero() { + 0 + } else { + remaining.as_millis().clamp(1, libc::c_int::MAX as u128) as libc::c_int + } +} + #[cfg(unix)] fn run_unix( running: Arc, pd_verifier: Arc, - free_enabled: Arc, + charging_mode: Arc, + config_event: Arc, + stop_event: Arc, ) -> Result<()> { - // 每线程独立创建 inotify(监控 free 文件),与 uevent 共用同一 epoll: - // free=0 时也无限阻塞在 epoll_wait,由 free 文件 inotify 事件唤醒,实现零周期唤醒 - let file_monitor = FileMonitor::new()?; - file_monitor.add_watch(FREE_FILE, IN_MODIFY | IN_CLOSE_WRITE)?; - file_monitor.add_inotify_to_epoll()?; - let uevent_sock = FileMonitor::create_uevent_monitor()?; - if let Err(e) = file_monitor.add_fd_to_epoll( - uevent_sock, - (libc::EPOLLIN | libc::EPOLLPRI) as u32, - uevent_sock as u64, - ) { - unsafe { - libc::close(uevent_sock); - } - return Err(e); - } - - info!( - "[{}] 开始通过uevent监控qcom状态: {}", - utils::get_current_thread_name(), - PD_VERIFIED_PATH - ); - - // free 暂停状态用本线程本地变量维护:仅初始化时读取共享原子, - // 之后由 inotify 唤醒时直接读 free 文件作为权威状态。 - // (free_file.rs 更新共享原子前有约100ms延迟,若依赖原子可能漏掉 0↔1 切换) - let mut enabled = free_enabled.load(Ordering::Relaxed); - if !enabled { - // 启动即暂停:从 epoll 移除 uevent socket,暂停期间仅由 free 文件 inotify 唤醒 - file_monitor.remove_fd_from_epoll(uevent_sock)?; + let event_monitor = FileMonitor::new()?; + + let mut mode = ChargingMode::from_raw(charging_mode.load(Ordering::Acquire)); + let mut uevent_registered = mode != ChargingMode::Native; + let setup = event_monitor + .add_fd_to_epoll( + stop_event.raw_fd(), + libc::EPOLLIN as u32, + stop_event.raw_fd() as u64, + ) + .and_then(|()| { + event_monitor.add_fd_to_epoll( + config_event.raw_fd(), + libc::EPOLLIN as u32, + config_event.raw_fd() as u64, + ) + }) + .and_then(|()| { + if uevent_registered { + event_monitor.add_fd_to_epoll( + uevent_sock, + (libc::EPOLLIN | libc::EPOLLPRI) as u32, + uevent_sock as u64, + ) + } else { + Ok(()) + } + }); + if let Err(error) = setup { + unsafe { libc::close(uevent_sock) }; + return Err(error); } - // 金标动画广播伪造:会话状态由本线程(qcom)驱动,broadcast-forger 线程负责发送 + info!("通过uevent与短时协商定时器监控qcom状态"); + let mut attached = is_attached()?; + let mut detach_deadline = None; + let mut phase = match (mode, attached) { + (ChargingMode::Automatic, true) => { + AutoPhase::WaitingNative(Instant::now() + NATIVE_NEGOTIATION_TIMEOUT) + } + _ => AutoPhase::Idle, + }; + let mut uevent_recheck_deadline = None; + let mut public_retry_attempted = false; + let mut charger_detach_deadline = None; + + // Preserve upstream's SystemUI gold-label/100 W broadcast feature. The + // worker is only activated for a charging session and exits with the daemon. let session_gen = Arc::new(AtomicU32::new(0)); let session_active = Arc::new(AtomicBool::new(false)); - let broadcast_forger = Arc::new(BroadcastForger); - spawn_broadcast_forger_worker( + let broadcast_session_event = Arc::new(EventFd::new()?); + let broadcast_stop_event = Arc::new(EventFd::new()?); + let broadcast_handle = spawn_broadcast_forger_worker( Arc::clone(&running), Arc::clone(&session_gen), Arc::clone(&session_active), - Arc::clone(&broadcast_forger), + Arc::clone(&broadcast_session_event), + Arc::clone(&broadcast_stop_event), + Arc::new(BroadcastForger), ); - - let mut eintr_count: u64 = 0; - let mut eagain_count: u64 = 0; let mut charging_session_active = false; - // 启动时若已处于充电状态(如开机前已插电):初始化充电会话并触发金标动画广播伪造 - if enabled + if uevent_registered + && attached && FileMonitor::read_file_content(BATTERY_STATUS_PATH).unwrap_or_default() == "Charging" { - start_charging_session(&mut charging_session_active, &session_gen, &session_active); - info!("[qcom] 启动时已处于充电状态,初始化充电会话并触发金标动画广播伪造"); + start_charging_session( + &mut charging_session_active, + &session_gen, + &session_active, + &broadcast_session_event, + ); } - let mut last_interrupt_report = std::time::Instant::now(); - let interrupt_report_interval = std::time::Duration::from_secs(60 * 60 * 10); - let mut events = [libc::epoll_event { events: 0, u64: 0 }; 10]; + let mut events = [libc::epoll_event { events: 0, u64: 0 }; 8]; while running.load(Ordering::Relaxed) { - let nfds = match file_monitor.wait_events(&mut events, -1) { - Ok(nfds) => nfds, - Err(err) => { - match err.raw_os_error() { - Some(code) if code == libc::EINTR || code == libc::EAGAIN => { - if code == libc::EINTR { - eintr_count += 1; - } else { - eagain_count += 1; - } - - let now = std::time::Instant::now(); - if now.duration_since(last_interrupt_report) >= interrupt_report_interval - && (eintr_count > 0 || eagain_count > 0) - { - debug!( - "epoll_wait暂时中断统计(最近{}秒): EINTR={}次, EAGAIN={}次", - interrupt_report_interval.as_secs(), - eintr_count, - eagain_count - ); - eintr_count = 0; - eagain_count = 0; - last_interrupt_report = now; - } - } - Some(code) => { - error!("epoll_wait错误(code={}),5秒后重试:{}", code, err); - std::thread::sleep(std::time::Duration::from_millis(5000)); - } - None => { - error!("epoll_wait错误(未知code),5秒后重试:{}", err); - std::thread::sleep(std::time::Duration::from_millis(5000)); - } + let nfds = match event_monitor.wait_events( + &mut events, + epoll_timeout( + phase, + detach_deadline, + uevent_recheck_deadline, + charger_detach_deadline, + ), + ) { + Ok(count) => count, + Err(error) => { + if matches!(error.raw_os_error(), Some(code) if code == libc::EINTR || code == libc::EAGAIN) + { + continue; } + error!("qcom epoll_wait失败: {}", error); + thread::sleep(Duration::from_secs(1)); continue; } }; - if nfds <= 0 { - continue; + let ready = &events[..nfds as usize]; + if ready + .iter() + .any(|event| event.u64 == stop_event.raw_fd() as u64) + { + stop_event.clear()?; + break; } - // 优先处理 free 文件 inotify 事件,刷新 enabled 后再处理 uevent, - // 保证同一批事件中 free=0 时 uevent 不会被误处理 - if events + if ready .iter() - .take(nfds as usize) - .any(|ev| ev.u64 == file_monitor.inotify_fd as u64) + .any(|event| event.u64 == config_event.raw_fd() as u64) { - let mut inotify_buffer = [0u8; 1024]; - let bytes_read = unsafe { - libc::read( - file_monitor.inotify_fd, - inotify_buffer.as_mut_ptr() as *mut std::os::raw::c_void, - inotify_buffer.len(), - ) - }; - - if bytes_read > 0 { - let bytes_read = bytes_read as usize; - let event_size = std::mem::size_of::(); - let mut offset = 0usize; - let mut close_write_seen = false; - while offset + event_size <= bytes_read { - let event_ptr = unsafe { - inotify_buffer.as_ptr().add(offset) as *const libc::inotify_event - }; - let event = unsafe { &*event_ptr }; - if (event.mask & libc::IN_CLOSE_WRITE) != 0 { - close_write_seen = true; - } - offset += event_size + event.len as usize; + config_event.clear()?; + if matches!(phase, AutoPhase::Suspended(_) | AutoPhase::PublicEnabled(_)) { + set_input_suspended(false)?; + } + let new_mode = ChargingMode::from_raw(charging_mode.load(Ordering::Acquire)); + let should_monitor_uevents = new_mode != ChargingMode::Native; + if should_monitor_uevents != uevent_registered { + if should_monitor_uevents { + event_monitor.add_fd_to_epoll( + uevent_sock, + (libc::EPOLLIN | libc::EPOLLPRI) as u32, + uevent_sock as u64, + )?; + } else { + event_monitor.remove_fd_from_epoll(uevent_sock)?; } - - if close_write_seen { - // 直接读 free 文件内容作为权威状态 - let new_enabled = FileMonitor::read_file_content(FREE_FILE)? == "1"; - if new_enabled != enabled { - if new_enabled { - // 恢复:把 uevent socket 重新加入 epoll - file_monitor.add_fd_to_epoll( - uevent_sock, - (libc::EPOLLIN | libc::EPOLLPRI) as u32, - uevent_sock as u64, - )?; - info!("[qcom] free文件恢复为1,重新启动PD验证节点监控"); - // 恢复时若已处于充电状态(free=0期间未跟踪会话),补触发金标动画广播伪造 - if !charging_session_active - && FileMonitor::read_file_content(BATTERY_STATUS_PATH) - .unwrap_or_default() - == "Charging" - { - start_charging_session( - &mut charging_session_active, - &session_gen, - &session_active, - ); - info!("[qcom] free恢复时已处于充电状态,触发金标动画广播伪造"); - } - } else { - // 暂停:从 epoll 移除 uevent socket,暂停期间不再被 uevent 唤醒 - file_monitor.remove_fd_from_epoll(uevent_sock)?; - info!("[qcom] free文件为0,暂停PD验证节点监控"); - } - enabled = new_enabled; - } + uevent_registered = should_monitor_uevents; + info!("[qcom] uevent监控状态: {}", uevent_registered); + } + mode = new_mode; + attached = is_attached()?; + detach_deadline = None; + public_retry_attempted = false; + charger_detach_deadline = None; + phase = match (mode, attached) { + (ChargingMode::Automatic, true) => { + AutoPhase::WaitingNative(Instant::now() + NATIVE_NEGOTIATION_TIMEOUT) } + _ => AutoPhase::Idle, + }; + if uevent_registered && attached { + start_charging_session( + &mut charging_session_active, + &session_gen, + &session_active, + &broadcast_session_event, + ); + } else { + stop_charging_session( + &mut charging_session_active, + &session_active, + &broadcast_session_event, + ); } + debug!("qcom收到模式变化: {:?}", mode); } - // free=0 时 uevent socket 已从 epoll 移除,不会因 uevent 被唤醒; - // 同一批事件中残留的 uevent 事件在暂停态直接跳过,不读取 - for ev in events.iter().take(nfds as usize) { - if ev.u64 != uevent_sock as u64 || !enabled { - continue; - } - + if uevent_registered && ready.iter().any(|event| event.u64 == uevent_sock as u64) { + // Drain every queued netlink datagram so epoll cannot spin on stale events. let mut buffer = [0u8; 4096]; - let bytes_read = unsafe { - libc::recv( - uevent_sock, - buffer.as_mut_ptr() as *mut std::os::raw::c_void, - buffer.len(), - libc::MSG_DONTWAIT, - ) - }; - - if bytes_read <= 0 { - continue; + loop { + let read = unsafe { + libc::recv( + uevent_sock, + buffer.as_mut_ptr().cast::(), + buffer.len(), + libc::MSG_DONTWAIT, + ) + }; + if read <= 0 { + break; + } } + // The first read below may race the driver update. Arm one bounded + // follow-up read; do not move an existing deadline for noisy bursts. + uevent_recheck_deadline.get_or_insert_with(|| Instant::now() + UEVENT_SETTLE_DELAY); + } - let uevent_data = String::from_utf8_lossy(&buffer[..bytes_read as usize]); - - // 提取POWER_SUPPLY_STATUS - let fields = uevent_data.split(['\0', '\n']); - let status = fields - .clone() - .find(|field| field.starts_with("POWER_SUPPLY_STATUS=")) - .and_then(|field| field.split_once('=').map(|(_, value)| value)); - - let mut should_set_node = false; + if uevent_recheck_deadline.is_some_and(|deadline| Instant::now() >= deadline) { + uevent_recheck_deadline = None; + } - // 充电过程中不强制写入pd_verifed: - // - 小米原装充电头:内核通过verify_process自行管理pd_verifed - // (verify结束后内核自己设pd_verifed=1),反复写入会干扰MIPPS握手 - // - 公版PPS充电头:内核不碰pd_verifed,依赖启动时设置的值 - // 仅在拔出(Discharging)时设置pd_verifed=1,为下次插电准备 - if let Some("Discharging") = status { - if charging_session_active { - info!( - "[qcom] 检测到Charging→Discharging状态跳变,设置pd_verifed=1为下次插电准备" + let physically_attached = is_attached()?; + if attached && !physically_attached { + let deadline = detach_deadline.get_or_insert(Instant::now() + DETACH_DEBOUNCE); + if Instant::now() >= *deadline { + attached = false; + detach_deadline = None; + if matches!(phase, AutoPhase::Suspended(_) | AutoPhase::PublicEnabled(_)) { + set_input_suspended(false)?; + } + if mode == ChargingMode::Automatic { + pd_verifier.set_pd_verified(false)?; + info!("[自动] 已拔出,恢复小米协议优先基线"); + } + public_retry_attempted = false; + charger_detach_deadline = None; + stop_charging_session( + &mut charging_session_active, + &session_active, + &broadcast_session_event, + ); + phase = AutoPhase::Idle; + } + } else if physically_attached { + detach_deadline = None; + if !attached { + attached = true; + public_retry_attempted = false; + charger_detach_deadline = None; + if mode == ChargingMode::Automatic { + phase = AutoPhase::WaitingNative(Instant::now() + NATIVE_NEGOTIATION_TIMEOUT); + info!("[自动] 检测到连接,等待小米协议认证"); + } + if mode != ChargingMode::Native { + start_charging_session( + &mut charging_session_active, + &session_gen, + &session_active, + &broadcast_session_event, ); - should_set_node = true; - stop_charging_session(&mut charging_session_active, &session_active); } - } else if let Some("Charging") = status - && !charging_session_active - { - start_charging_session(&mut charging_session_active, &session_gen, &session_active); - debug!("[qcom] 检测到充电会话开始"); } + } - if should_set_node { - let pd_content = FileMonitor::read_file_content(PD_VERIFIED_PATH)?; - if pd_content == "0" { - info!("[qcom] 设置pd_verifed=1"); - pd_verifier.set_pd_verified(true)?; + if mode != ChargingMode::Automatic || !attached { + charger_detach_deadline = None; + continue; + } + + // A USB meter can keep Type-C physically attached while its upstream + // charger is unplugged. Treat a stable loss of the charger as the end + // of the retry session, but never do this during our intentional power + // suspension where the same node changes are expected briefly. + if matches!(phase, AutoPhase::Settled) && public_retry_attempted { + let verified = FileMonitor::read_file_content(PD_VERIFIED_PATH)?; + let usb_type = FileMonitor::read_file_content(USB_REAL_TYPE_PATH)?; + if verified == "0" && usb_type == "Unknown" { + let deadline = + charger_detach_deadline.get_or_insert_with(|| Instant::now() + DETACH_DEBOUNCE); + if Instant::now() >= *deadline { + public_retry_attempted = false; + charger_detach_deadline = None; + info!("[自动] 检测到充电器已从转接设备断开,允许下次公版PPS重连"); } + } else { + charger_detach_deadline = None; } + } else { + charger_detach_deadline = None; } - } - if eintr_count > 0 || eagain_count > 0 { - debug!( - "epoll_wait暂时中断统计(线程退出前): EINTR={}次, EAGAIN={}次", - eintr_count, eagain_count - ); + phase = match phase { + AutoPhase::WaitingNative(deadline) => { + if FileMonitor::read_file_content(PD_VERIFIED_PATH)? == "1" { + info!("[自动] 小米协议认证成功,保持原生协商"); + AutoPhase::Settled + } else if Instant::now() >= deadline { + let usb_type = FileMonitor::read_file_content(USB_REAL_TYPE_PATH)?; + if usb_type == "PD_PPS" { + info!("[自动] 未检测到小米认证,开始一次公版PPS软件重连"); + public_retry_attempted = true; + set_input_suspended(true)?; + AutoPhase::Suspended(Instant::now() + RECONNECT_STEP_DELAY) + } else { + warn!("[自动] 未认证且接口类型为{},本次不强制切换", usb_type); + AutoPhase::Settled + } + } else { + AutoPhase::WaitingNative(deadline) + } + } + AutoPhase::Suspended(deadline) if Instant::now() >= deadline => { + pd_verifier.set_pd_verified(true)?; + AutoPhase::PublicEnabled(Instant::now() + RECONNECT_STEP_DELAY) + } + AutoPhase::PublicEnabled(deadline) if Instant::now() >= deadline => { + set_input_suspended(false)?; + info!("[自动] 公版PPS软件重连完成,本次连接不再重试"); + AutoPhase::Settled + } + AutoPhase::Settled if !public_retry_attempted => { + let verified = FileMonitor::read_file_content(PD_VERIFIED_PATH)?; + let usb_type = FileMonitor::read_file_content(USB_REAL_TYPE_PATH)?; + if verified != "1" && usb_type == "PD_PPS" { + info!("[自动] 已连接设备后检测到公版PPS,开始一次软件重连"); + public_retry_attempted = true; + set_input_suspended(true)?; + AutoPhase::Suspended(Instant::now() + RECONNECT_STEP_DELAY) + } else { + AutoPhase::Settled + } + } + other => other, + }; } + if matches!(phase, AutoPhase::Suspended(_) | AutoPhase::PublicEnabled(_)) { + let _ = set_input_suspended(false); + } unsafe { libc::close(uevent_sock); } - + if let Err(error) = broadcast_stop_event.notify() { + error!("通知broadcast-forger线程停止失败: {}", error); + } + if let Err(error) = broadcast_handle.join() { + error!("broadcast-forger线程join失败: {:?}", error); + } Ok(()) } -/// 开始一次充电会话并触发金标动画广播伪造(幂等:已在会话中时不重复触发) #[cfg(unix)] fn start_charging_session( charging_session_active: &mut bool, session_gen: &AtomicU32, session_active: &AtomicBool, + session_event: &EventFd, ) { if !*charging_session_active { *charging_session_active = true; session_active.store(true, Ordering::Relaxed); session_gen.fetch_add(1, Ordering::Relaxed); + if let Err(error) = session_event.notify() { + warn!("通知broadcast-forger会话开始失败: {}", error); + } } } -/// 结束充电会话,停止金标动画广播补发 #[cfg(unix)] -fn stop_charging_session(charging_session_active: &mut bool, session_active: &AtomicBool) { +fn stop_charging_session( + charging_session_active: &mut bool, + session_active: &AtomicBool, + session_event: &EventFd, +) { *charging_session_active = false; session_active.store(false, Ordering::Relaxed); + if let Err(error) = session_event.notify() { + warn!("通知broadcast-forger会话结束失败: {}", error); + } } diff --git a/src/pd/broadcast_forger.rs b/src/pd/broadcast_forger.rs index 69c5fc3..1892f75 100644 --- a/src/pd/broadcast_forger.rs +++ b/src/pd/broadcast_forger.rs @@ -3,6 +3,7 @@ use crate::common::constants::{ }; use crate::common::utils; use crate::monitoring::FileMonitor; +use crate::platform::EventFd; use log::{debug, info, warn}; use std::process::{Command, Stdio}; use std::sync::Arc; @@ -37,9 +38,6 @@ const FULL_POWER_DISPLAY_W: u32 = 100; // 弱充电头判定阈值:Vbus 电压低于此值视为弱充(<45W)不伪造 const MIN_HIGH_POWER_VOLTAGE_UV: u64 = 12_000_000; -// 会话轮询间隔:兼顾充电会话开始(含重启)的检测延迟与 CPU 占用 -const SESSION_POLL_INTERVAL: Duration = Duration::from_millis(100); - /// 金标动画广播伪造器 /// /// 伪造小米原装 MIPPS 头的 `ACTION_QUICK_CHARGE_TYPE` 广播,让 SystemUI 对公版 PPS 头 @@ -246,6 +244,8 @@ pub fn spawn_broadcast_forger_worker( running: Arc, session_gen: Arc, session_active: Arc, + session_event: Arc, + stop_event: Arc, forger: Arc, ) -> thread::JoinHandle<()> { thread::Builder::new() @@ -255,12 +255,46 @@ pub fn spawn_broadcast_forger_worker( info!("[{}] 启动金标动画广播伪造线程...", thread_name); let mut burst_done_for: Option = None; + let mut poll_fds = [ + libc::pollfd { + fd: session_event.raw_fd(), + events: libc::POLLIN, + revents: 0, + }, + libc::pollfd { + fd: stop_event.raw_fd(), + events: libc::POLLIN, + revents: 0, + }, + ]; while running.load(Ordering::Relaxed) { - // 会话未激活:等待下一次充电会话(新会话需重新执行首轮发送) + let result = unsafe { libc::poll(poll_fds.as_mut_ptr(), poll_fds.len() as _, -1) }; + if result == -1 { + let error = std::io::Error::last_os_error(); + if error.raw_os_error() == Some(libc::EINTR) { + continue; + } + warn!("[broadcast-forger] poll失败: {}", error); + break; + } + + if poll_fds[1].revents & libc::POLLIN != 0 { + if let Err(error) = stop_event.clear() { + warn!("[broadcast-forger] 清除停止事件失败: {}", error); + } + break; + } + + if poll_fds[0].revents & libc::POLLIN == 0 { + continue; + } + if let Err(error) = session_event.clear() { + warn!("[broadcast-forger] 清除会话事件失败: {}", error); + } + if !session_active.load(Ordering::Relaxed) { burst_done_for = None; - thread::sleep(SESSION_POLL_INTERVAL); continue; } @@ -270,10 +304,7 @@ pub fn spawn_broadcast_forger_worker( // (让超级岛直接显示 100W MAX,避免多余的"快充中"回退通知) burst_done_for = Some(generation); forger.send_burst(); - continue; } - - thread::sleep(SESSION_POLL_INTERVAL); } }) .expect("创建broadcast-forger线程失败") diff --git a/src/platform.rs b/src/platform.rs index 3cc45e0..8892fce 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -1,3 +1,5 @@ +pub mod event_fd; pub mod signal; -pub use signal::install_signal_handlers; +pub use event_fd::EventFd; +pub use signal::{SignalWaiter, install_signal_handlers}; diff --git a/src/platform/event_fd.rs b/src/platform/event_fd.rs new file mode 100644 index 0000000..fc78026 --- /dev/null +++ b/src/platform/event_fd.rs @@ -0,0 +1,82 @@ +use anyhow::Result; + +/// A small eventfd wrapper used to wake a thread blocked in epoll. +pub struct EventFd { + #[cfg(unix)] + fd: libc::c_int, +} + +impl EventFd { + pub fn new() -> Result { + #[cfg(unix)] + { + let fd = unsafe { libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) }; + if fd == -1 { + return Err(std::io::Error::last_os_error().into()); + } + Ok(Self { fd }) + } + + #[cfg(not(unix))] + { + Ok(Self {}) + } + } + + #[cfg(unix)] + pub fn raw_fd(&self) -> libc::c_int { + self.fd + } + + /// Mark the event as ready. `eventfd` coalesces repeated notifications. + pub fn notify(&self) -> std::io::Result<()> { + #[cfg(unix)] + { + let value: u64 = 1; + let result = unsafe { + libc::write( + self.fd, + (&value as *const u64).cast::(), + std::mem::size_of::(), + ) + }; + if result == -1 { + let error = std::io::Error::last_os_error(); + if error.kind() != std::io::ErrorKind::WouldBlock { + return Err(error); + } + } + } + + Ok(()) + } + + /// Clear all pending notifications without blocking. + #[cfg(unix)] + pub fn clear(&self) -> std::io::Result<()> { + let mut value = 0u64; + let result = unsafe { + libc::read( + self.fd, + (&mut value as *mut u64).cast::(), + std::mem::size_of::(), + ) + }; + if result == -1 { + let error = std::io::Error::last_os_error(); + if error.kind() != std::io::ErrorKind::WouldBlock { + return Err(error); + } + } + Ok(()) + } +} + +impl Drop for EventFd { + fn drop(&mut self) { + #[cfg(unix)] + unsafe { + libc::close(self.fd); + } + } +} diff --git a/src/platform/signal.rs b/src/platform/signal.rs index 8dcbe5e..400d400 100644 --- a/src/platform/signal.rs +++ b/src/platform/signal.rs @@ -3,11 +3,49 @@ use std::sync::Arc; use std::sync::atomic::AtomicBool; #[cfg(unix)] -use std::sync::atomic::AtomicPtr; +use std::sync::atomic::{AtomicI32, AtomicPtr}; + +use crate::platform::EventFd; #[cfg(unix)] static RUNNING_FLAG_PTR: AtomicPtr = AtomicPtr::new(std::ptr::null_mut()); +#[cfg(unix)] +static SIGNAL_EVENT_FD: AtomicI32 = AtomicI32::new(-1); + +pub struct SignalWaiter { + event: Arc, +} + +impl SignalWaiter { + pub fn new() -> anyhow::Result { + Ok(Self { + event: Arc::new(EventFd::new()?), + }) + } + + #[cfg(unix)] + pub fn wait(&self) -> std::io::Result<()> { + loop { + let mut poll_fd = libc::pollfd { + fd: self.event.raw_fd(), + events: libc::POLLIN, + revents: 0, + }; + let result = unsafe { libc::poll(&mut poll_fd, 1, -1) }; + if result > 0 { + self.event.clear()?; + return Ok(()); + } else if result == -1 { + let error = std::io::Error::last_os_error(); + if error.kind() != std::io::ErrorKind::Interrupted { + return Err(error); + } + } + } + } +} + #[cfg(unix)] /// # Safety /// 该函数作为异步信号处理器调用,需要保证 `RUNNING_FLAG_PTR` 指向的内存有效。 @@ -18,6 +56,18 @@ unsafe extern "C" fn termination_signal_handler(_sig: libc::c_int) { (*ptr).store(false, std::sync::atomic::Ordering::Relaxed); } } + + let event_fd = SIGNAL_EVENT_FD.load(std::sync::atomic::Ordering::Relaxed); + if event_fd != -1 { + let value: u64 = 1; + unsafe { + libc::write( + event_fd, + (&value as *const u64).cast::(), + std::mem::size_of::(), + ); + } + } } #[cfg(unix)] @@ -25,17 +75,18 @@ unsafe extern "C" fn termination_signal_handler(_sig: libc::c_int) { /// /// # Safety /// 调用者必须保证传入的 `running` 在整个信号处理期间保持有效。 -pub fn install_signal_handlers(running: &Arc) { +pub fn install_signal_handlers(running: &Arc, waiter: &SignalWaiter) { RUNNING_FLAG_PTR.store( Arc::as_ptr(running) as *mut AtomicBool, std::sync::atomic::Ordering::SeqCst, ); + SIGNAL_EVENT_FD.store(waiter.event.raw_fd(), std::sync::atomic::Ordering::SeqCst); unsafe { let mut action: libc::sigaction = std::mem::zeroed(); action.sa_sigaction = termination_signal_handler as *const () as usize; libc::sigemptyset(&mut action.sa_mask); - action.sa_flags = libc::SA_RESTART; + action.sa_flags = 0; if libc::sigaction(libc::SIGINT, &action, std::ptr::null_mut()) == -1 { error!("注册SIGINT处理器失败: {}", std::io::Error::last_os_error()); @@ -48,4 +99,4 @@ pub fn install_signal_handlers(running: &Arc) { } #[cfg(not(unix))] -pub fn install_signal_handlers(_: &Arc) {} +pub fn install_signal_handlers(_: &Arc, _: &SignalWaiter) {}