Repository navigation
Releases: aapplle/op13-battery-unlock
Release list
一加13解容 uv2800 v11
一加 13 解容模块 uv2800 v11
v11 是在 v10 基础上做的一次全面重构:内核模块、用户态脚本、状态文件和文档都重新整理过。
以下为两个改动最大、用户能直接感觉到的变化。
一、越狱模式下不用再插拔充电器
v10 走越狱路线时有一个躲不掉的动作:模块注入之后要插一下充电器再拔掉,否则模块拿不到电量计的设备指针,
也就等于没生效。这一步用户能感觉到,而且很容易漏做。
v11 去掉了这个动作,办法是模块自己制造一次驱动调用:
- 模块往
deep_dischg_counts写入同一个值(模块源目录/log.sh里的uv_capture_dev)。 - 驱动收到这次写入,会调用
oplus_fg_set_batt_deep_dischg_count;模块在这个函数的入口挂了挂钩
(内核源码/uv2800.c里的kp_ddrc),顺手把设备指针记下来。 - 万一还是没拿到,
模块源目录/action.sh会再读一次 ADSP 确认;仍然拿不到就提示重启,
不会拿猜出来的值往下走。
关机电压这一侧本来就跟插拔无关:挂钩直接改寄存器,vbat_uv 读到的就是设定值本身,改参数立刻跟随、
没有延迟。ADSP 终止电压则改由模块主动回写,不再等驱动来纠偏 —— 驱动只在"投票目标和当前值不一样"时
才调 setter,平时两者相同,被动拦截根本不会触发。
所以现在两种部署方式(标准启动、越狱注入)装完就能用,全程不碰充电器。
二、原厂截止电压改成完全复刻厂商算法
点「执行」按钮和卸载模块时,都要把电量计的终止电压恢复成这台设备当前的原厂值。这个值不是固定数字,
驱动是按"深度放电比率档 × 温度档"两个维度查设备树里的 ddrc_strategy 子树算出来的。
v10 用的是另一套算法:查 deep_spec,term_coeff 那张表,用原始计数去选行。那张表只决定驱动用哪张
ddrc 表,不是电压来源,按那套算出来的原厂值是错的。v11 通过进一步逆向,完整
复刻了原厂计算方式,具体实现在 模块源目录/log.sh 的 uv_dt_orig:
输入:counts = 深度放电计数,cc = 电池循环次数,
cali = 深度放电计数校准值(节点不存在就当 0),temp = 电池温度(0.1 ℃)
① ratio = 10 × counts / cc(只在 0 < cc < 5000 时做除法)
② region = ratio 依次和 oplus,ratio_range 比,大于等于就抬一档(0=min … 5=high)
③ index_t= temp 和 oplus,temp_range 比,小于等于就归下一档(0=cold … 3=warm)
④ 表 = <电池型号>/ddrc_strategy/strategy_ratio_range_<region>/strategy_temp_<温度>
每行 4 个 32 位整数(大端):阈值、关机电压、终止电压、序号
⑤ 选行 k = 满足 max(0, 该行阈值 − cali) ≤ cc 的最大行号(一行都不满足就取第 0 行)
⑥ 结果 = 第 k 行的第 3 个字段
新增自检机制:算不出来就返回空,不会盲目写入。推算过程带完整自检 —— 输入必须是纯数字;计数或循环次数超过
一百万、校准值超过一万判失败;校准值节点不存在按 0 处理,但节点存在而值非法判失败;两个输入都是 0
判失败;ratio_range 必须正好 5 项、temp_range 必须正好 3 项;目标表字节数是 16 的倍数、行数在
1 到 8 之间;每行两个电压列都必须落在 2000–5000;第 0 行阈值必须是 0 且阈值单调不降;最终结果必须落在
2900–3500。任何一项不过就返回空字符串,调用方各自走安全路径(开机脚本打警告、手动恢复脚本退出、
卸载脚本跳过写入),不会拿猜测值回填。
三、其它变化
- 关机电压默认 2800 mV,范围 2800–3060 mV;ADSP 终止电压按「关机电压 − max(0, 3060 − 关机电压)」派生,默认 2540 mV。
- 六个参数接口:
uv_target_mv、uv_adsp_mv、adsp_read、adsp_write、resume、adsp_debug。 - 跨 ColorOS 版本靠两套 profile 按指令字自动选择;加载时会校验四条关键指令字,对不上就拒绝加载。
- 越狱路线下的电量挂载走 PID 1 的全局命名空间,卸载时会解绑。
- 给使用者看的说明在
README.md,技术细节在项目文档.md,构建与上游来源在THIRD-PARTY-NOTICES.md。
Full Changelog: v10.8...v11
一加13解容 v10.8
v10.8
在 v10.7 基础上,针对 KernelSU 软重启(
ksud soft-reboot,内核不重启)的极端场景做了系统性加固,
并修复原值备份被污染导致「执行」回写错误值的问题。
🔴 软重启相关加固
-
skip标记提前到 ADSP 回写【之前】(重要)
service.sh的 60 秒探测循环与后台adsp_retry会在等待期间把刚恢复的原值又写回解耦目标。
实测:回写后 1 秒就被撤销,用户以为恢复失败。现在skip先落盘,两个循环立刻停手。 -
post-fs-data.sh:模块已在内存则跳过rmmod+insmod
软重启会重跑post-fs-data,旧逻辑强制重载导致驱动指针uv_dev被清空 → 用户必须重新插拔。
现在检测到已加载就跳过,uv_dev保留。 -
清理跨软重启残留的
adsp_retry
后台进程不会被soft-reboot杀掉(实测软重启后同时存在多个)。改用 PID 文件 + 扫描/proc兜底;
匹配模式为*uv2800/service.sh*,不会误杀其他模块(已用假模块实测验证)。 -
uninstall.sh/action.sh:umount改用nsenter -t 1 -m
越狱模式下service.sh用nsenter在 PID 1 全局命名空间 bind,普通umount够不到那层绑定,
导致卸载后仍显示真实 SOC。同时注意:/proc/mounts只显示最顶层,不能用来判断是否绑定。
🟠 备份污染防护(P0)
service.sh新增backup_orig()统一入口:读到<=2900时拒绝备份
(C16 的 DTterm_coeff最低档为 3000,真原值必然 >2900;<=2900只能是本模块写过的值)action.sh检测adsp_orig.txt <=2900则隔离为adsp_orig.bad,改用 DT 查表兜底
→ 即使旧版本已污染备份,升级后也能自愈
🟢 日志
- 新增
log.sh:关键日志三路输出(stdout +/dev/kmsg+ 备份目录uv2800.log)
日志不随卸载清除,便于用户反馈问题;超过 3000 行自动保留后 1500 行 uninstall.sh:两个分支都清理adsp_target,保留uv2800.log与adsp_orig.txtaction.sh:okv=skip分支(回写后缓存已相等时不再提示插拔),结尾提示三态区分
🧪 实测验证(C16 V16.1.0 + KernelSU 3.3.0-53 late-load)
覆盖 7 项软重启极端场景 + 完整回归流程,全部通过:
| 场景 | 结果 |
|---|---|
| 未插拔直接软重启 | ✅ 模块不重载、uv_dev 保持 |
| 连续两次软重启 | ✅ 全部修复生效 |
| 插拔后立即软重启 | ✅ 无数据丢失、后台补写成功 |
disable + 软重启 |
✅ 模块留内存、脚本不跑 |
remove + 软重启 |
✅ 目录删除、日志保留 |
| 备份目录被删除 | ✅ 自动重建 |
adsp_target 改动 |
✅ 按新目标写入 |
安装包:op13-battery-unlock-v10.8.zip
主文档:PROJECT-vbat_uv-2800.md|历史档案:PROJECT-history.md
一加13解容 v10.6
v10.6
修复
- 解耦写入路径回归主动
adsp_write(重要)
v10.5 引入的adsp_force被动拦截方案经实测证伪:驱动只在「vote 目标 ≠ 当前 ADSP」时才调用 setter 写 term,日常 vote 目标等于当前值,驱动根本不写 → setter hook 全程不触发 → 解耦在标准/越狱两模式都无效(实测 ADSP 停在 3250 不动)。
本版让内核模块回归原始 v10(无adsp_force参数),service.sh恢复主动adsp_write2600。 - 恢复
adsp_retry后台补写:越狱模式下插拔充电器后自动补写 ADSP,并补做原值备份adsp_orig.txt。
修正
- 越狱(late-load)模式提示措辞:每次重启后插拔一次充电器即可。同一次开机内「安装解耦」与「卸载回写」共用这一次指针捕获,无需重复插拔。
文档
- 新增
battery_fcc节点逆向结论:该节点每次读都实时查询 gauge IC,查询失败时(oplus_get_gauge_type返回-ENOTSUPP)会回退到design_capacity,显示的不是电量计真实 fcc。验证解容效果请用驱动bs_update_data日志里的fcc。
实测验证(一加13 / ColorOS 17 / KernelSU 3.3.0)
- 越狱模式完整安装:ADSP 3250→2600、
vbat_uv2800、bind-mount、禁超级省电、原值备份 3250 ✅ - 越狱模式完整卸载:ADSP 回 3250、
vbat_uv回 3250、设备策略恢复 ✅ - 驱动 fcc:ADSP 3250 → 4352;ADSP 2600 → 4996 ✅
安装
用 KernelSU 管理器刷入 op13-battery-unlock-v10.6.zip,重启。
⚠️ 越狱(late-load)模式:每次重启后插拔一次充电器即可(同一次开机内无需重复插拔)。
⚠️ 卸载前请保证电量 >30%,先点「执行」按钮恢复默认再卸载。
一加13解容 v10.5
一加13解容 v10.5
越狱(late-load)模式解耦修复:不再依赖设备指针
问题(v10.3 的局限)
越狱模式下模块加载太晚,错过了驱动开机那次 vote,拿不到「驱动设备指针」(uv_dev)。
而 v10.3 的解耦(写 ADSP term = 2600)走的是 service.sh → adsp_write —— 依赖 uv_dev,
所以越狱模式下解耦失效:ADSP 停在 setter hook 强制的 2800,SOC/fcc 仍偏保守。
修复:把 setter hook 的强制值参数化
反汇编依据(COS15/16/17 一致,已逐条核实):
; oplus_gauge_term_voltage_vote_callback
+234 ldr x0, [x21, #0x30] ; x0 = *(x21+0x30)
+238 mov w1, w19 ; w1 = volt
+23c bl #0x1c1e44 ; -> oplus_mms_gauge_set_deep_term_volt
; 内部 blr x8 间接调用 -> oplus_fg_set_deep_term_volt所以 vote 一触发 → 必然走到模块 hook 的 setter —— 这条路径完全不依赖 uv_dev ✅
内核改动(约 25 行):
| 项 | v10 | v10.5 |
|---|---|---|
uv_term_set 强制值 |
固定 UV_TARGET_MV(2800) |
uv_adsp_force_mv(新参数,默认 2600) |
uv_term_get(决定关机电压) |
2800 | 2800(不变) |
| 新参数 | — | /sys/module/uv2800/parameters/adsp_force |
语义分离:
uv_term_get管「何时关机」(2800),uv_term_set管「电量计模型下限」(默认 2600)。
脚本改动
| 文件 | 改动 |
|---|---|
service.sh |
删掉后台重试(v10.3 的死等方案),改为把 adsp_target 同步到内核 adsp_force 参数 —— 不依赖 uv_dev,标准/越狱两模式统一 |
action.sh |
就绪检测仅越狱模式;未就绪提示插拔并等 60s,超时硬中止(exit 1),修掉 v10.3 的「假成功」 |
customize.sh(新增) |
安装时提示(仅越狱模式):需插拔一次充电器 + 无时间限制说明 |
late-load.sh |
同步说明 |
⚠️ 触发条件(重要)
vote 是事件驱动,不是定时器 —— 只在「充电状态变化」(最可靠 = 插拔一次充电器)或
「深度放电跨档」(极罕见)时触发。所以越狱模式仍需插拔一次充电器:
- 触发 vote → setter hook 写 2600(解耦,v10.5 起不依赖
uv_dev) - 同次 vote → getter 调用 → 捕获
uv_dev(卸载回写用) - 一次插拔,两件事同时完成;无时间限制,任意时刻插拔一次即可
- 不插拔不影响关机保护(getter hook 固定 2800 已生效)
升级方式
KernelSU 管理器直接刷入 op13-battery-unlock-v10.5.zip 覆盖,不需要卸载。
文件
op13-battery-unlock-v10.5.zip(md5bee07961dcb2499dcac302eac83cf711)
一加13解容 v10.2
一加13解容 v10.2
修复:解耦配置会静默丢失的问题(adsp_target 无人创建)
问题
adsp_target 是控制「电量计模型下限」的配置文件,但没有任何脚本会创建它:
| 脚本 | 行为 |
|---|---|
service.sh |
只读它;缺失时静默回落到默认 2800 |
action.sh |
删它(rm -f) |
uninstall.sh |
清空整个备份目录 |
后果:目录一旦被清,解耦(ADSP=2600)就悄悄失效 → 电量计模型下限回到 2800 →
0% 又会提前约 200mV 出现(2983mV 而不是 2865mV),而用户从界面上完全看不出来。
修复
- 默认值 2800 → 2600:解耦成为默认行为,不再是"需要手动配置的高级选项"
- 首次运行自动生成
adsp_target:缺失时写入默认值,状态可见可改
(想回到不解耦就手改成 2800;想更保守可写 3000) - 新增
oplusdevicepolicy异常检测:遇到Transaction too large
(/data/system下策略 XML 堆积导致)时打印明确提示并跳过,
不再无提示地等满 60 秒超时
验证(本机 ColorOS 17 实测)
ADSP term = 2600 mV
vbat_uv = 2800 mV
→ ✅ 已解耦(差 200 mV)
adsp_target = 2600(自动生成)
dmesg: uv2800: [adsp_write] 写 2600 mV,返回 0(强制仍生效)
升级方式
KernelSU 管理器直接刷入 op13-battery-unlock-v10.2.zip 覆盖,不需要卸载。
只改用户态脚本(
service.sh),内核模块uv2800.ko未变。
关于卸载与「原值记录」
uninstall.sh 会清空 /data/adb/uv2800_backup/(含 adsp_orig.txt)。
但这不会导致原值丢失 —— 两条路径都能拿到正确原值:
| 卸载方式 | 重装时 ADSP 里的值 | 结果 |
|---|---|---|
| 先点「执行」回写原值再卸载(推荐流程) | 原值(如 3250) | 与目标值 2600 不同 → 正确捕获为 adsp_orig.txt ✅ |
| 没点「执行」直接卸载 | 2600(模块写入值) | 与目标值相同 → service.sh 的守卫 [ "$cur" != "$ADSP_TARGET" ] 拦住不误捕获 ✅ |
第二种情况下 adsp_orig.txt 缺失,「执行」按钮会自动改用 DT 表实时计算兜底
(v10.1 起已修复 C15/C16 上查表失败的问题)。
关键:service.sh 只在「当前值 ≠ 目标值」时才把它当作原值备份 ——
恰好挡住了「把模块自己写入的值误当原值」这条错误路径。
文件
op13-battery-unlock-v10.2.zip(md5d657a7672fa1df16110409ccf76a69ed)
一加13解容 v10.1
一加13解容 v10.1
修复:「执行」按钮在 ColorOS 15/16 上计算失败(得到 -1)
问题
action.sh 计算原值的逻辑是「取 term_coeff 表里最后一条 count ≤ 当前深度放电次数 的电压」。
但当 deep_dischg_counts 小于表里第一档时,一行都命中不了 → 返回 -1 → 按钮报错。
| 版本 | 子节点表 count 档位 | count=562 时 |
|---|---|---|
| ColorOS 17(本机) | 1300 ~ 2000 | 命中 → 3250 ✅ |
| ColorOS 16 | 800 ~ 1200 | 无命中 → -1 ✗ |
| ColorOS 15 | 350 ~ 600 | 命中(但低 count 时同样会 -1)✗ |
修复(两处)
- 优先使用首次写入前的备份
/data/adb/uv2800_backup/adsp_orig.txt
—— 那是模块第一次改电量计之前存的真实原值,就是驱动自己算出来的值
(与本机驱动日志DEEP_COUNT_VOTER, volt = 3250完全一致),与 ColorOS 版本无关。 - DT 表计算增加兜底:count 低于第一档时取第一档电压(对齐驱动的下界行为),
不再返回-1。
验证
| 场景 | 修复前 | 修复后 |
|---|---|---|
有 adsp_orig.txt(正常) |
3250 | 3250(来源:备份)✅ |
| 备份丢失(C17 表) | 3250 | 3250(来源:DT 表)✅ |
| 备份丢失(C16 表,count=562) | -1 ✗ | 3000 ✅ |
| 备份丢失(C15 表,count=100) | -1 ✗ | 3000 ✅ |
| C15/C16 表,count 正常 | 正确 | 不变 ✅ |
升级方式
直接用 KernelSU 管理器刷入 op13-battery-unlock-v10.1.zip 覆盖即可,不需要卸载。
只改了
action.sh(用户态脚本),内核模块uv2800.ko未变。
文件
op13-battery-unlock-v10.1.zip(md5e2cf8003b03c00b21a9bce1d79f72960)
一加13解容 v10
一加13解容 v10
把一加 13(PJZ110)的关机截止电压从 3250mV 降到 2800mV,并让电量计原生按新的空电点重算 SOC。
不刷 dtbo、不改任何分区、不碰 system/vendor,卸载无残留。
效果
| 指标 | 原厂 | 解容后 |
|---|---|---|
| 关机电压 | 3250 mV | 2800 mV |
| 满容量 fcc | 4346 mAh | ~5032 mAh(+15.8%) |
| 0% 对应电压 | 2983 mV | 2865 mV |
| 0% → 关机 | 217 mV / 9 分钟 | 83 mV / 0.9 分钟 |
| 显示 | 官方平滑(13W 放电滞后 13 个点) | 电量计真实值 |
| 低电量 | 强制进超级省电 | 已禁用 |
安装
1) KernelSU 管理器刷入 一加13解容-v10.zip
2) 重启 ← 唯一需要的一次重启
3) 满电状态下拔插一次充电器(或充满一次)
4) 完成,电量计原生重算 fcc
卸载
1) 【必须】电量充足(>50%)时点模块的「执行」按钮(回写原值 + 恢复策略)
2) 删除模块 → 重启
⚠️ 低电压回写会让电量计进入钳位态(SOC 卡 0%,需充满一次恢复)
兼容性
- ColorOS 15 / 16 / 17(内核 6.6.30 / 6.6.89 / 6.6.118),自动适配
- KernelSU 标准模式 + late-load(越狱 / 临时 root)模式
- 指令字不匹配时拒绝加载,不会乱 hook
风险
- 2800mV 是硅碳负极放电下限,长期每次放到自动关机会加速电池老化
- COS15/16 的电量计 term→fcc 行为未经真机验证(最坏退化为「0% 提前」,不损坏硬件)
- 刷机有风险,请自行备份
文件
一加13解容-v10.zip—— KernelSU 模块(md507ae5c2baf3ad245691efce21c7ae550)
完整原理、逆向细节与踩坑清单见仓库文档 PROJECT-vbat_uv-2800.md。
License: GPL-2.0