Skip to content

Support smart charging and battery authentication status - #167

Open
petm5 wants to merge 2 commits into
linux-surface:v6.18-surface-develfrom
petm5:v6.18-surface-devel-battery-features
Open

petm5 wants to merge 2 commits into
linux-surface:v6.18-surface-develfrom
petm5:v6.18-surface-devel-battery-features

Conversation

@petm5

@petm5 petm5 commented Jul 7, 2026

Copy link
Copy Markdown

Added support for battery features reverse-engineered from the Windows drivers.

Authentic batteries in Surface devices are verified and paired with the motherboard during manufacturing or repair for quality assurance. Windows reads the authentication status and shows a warning message if the battery isn't genuine. This status can now be read from Linux.

Newer Surface devices limit the battery charge to 80% based on usage patterns. The Surface app on Windows has a button that disables the limit temporarily and charges the battery to 100%, but Linux previously had no way to control it. The battery driver now reports the smart charging status as a charge limit and lets users disable it by setting it to 100%. The charge limit is controlled by the SAM and gets re-enabled automatically after a few days, and re-enabling the limit manually doesn't seem to be possible.

Fixes linux-surface/linux-surface#1781, linux-surface/linux-surface#2070.

petm5 added 2 commits July 7, 2026 16:11
Authentic batteries in Surface devices are verified and paired with the
motherboard during manufacturing or repair for quality assurance. Read
the authentication status from the SAM and report it.
Newer Surface devices limit the battery charge to 80% based on usage
patterns. Report the charge limit status and disengage it if requested.

The charge limit is always controlled by the SAM and will be re-enabled
after a few days depending on use.
@Cloveian

Copy link
Copy Markdown

so about "re-enabling the limit manually doesn't seem to be possible", you actually can set it, it's just a different pair of battery commands ("user charge mode"). i dug them out of the windows drivers and tested both on my Surface Pro for Business 11th Edition with Intel (SAM 10.3.139) through /dev/surface/aggregator.

commands

all TC 0x02 (BAT), TID 0x01, IID 0x01

CID name (windows) payload response
0x09 get user charge mode none u32 modes_supported, u8 current_mode, u8 previous_mode, u8 settings[]
0x0a set user charge mode u8 mode, u8 settings[4] (5 bytes) none

modes (enum UserChargeMode): 0 adaptive, 1 user set limit, 2 no limit with timeout

  • mode 1: settings[0] = limit in percent
  • mode 2: settings[0..3] = u32 timeout in seconds
  • mode 0: all zeros

so 80% is 0a with 01 50 00 00 00, and adaptive is 00 00 00 00 00.

where it's from

  • SurfaceBatteryClient.sys (Surface Pro 11 Intel driver pack, 26.083.38253.0): BatteryCommunication_SetUserChargeMode builds a BAT request with CID 0x0a, no response flag, and a 5 byte payload it just copies from whatever called it
  • the Surface app's battery service (Services/BatteryDm.dll in Microsoft.SurfaceHub 76.9122.158.0, it's .NET): CommandIdGetUserChargeMode = 9, CommandIdSetUserChargeMode = 10, the Battery_Set_UserChargeMode struct (byte Mode; byte[4] ModeSettings, Pack=1), and BatteryUserChargeModeUserSetLimitMinPct = 50 / MaxPct = 80 / DefaultPct = 80

what it actually does (on mine)

  • get returns 10 bytes here (03 00 00 00 01 00 50 00 00 00): supported = adaptive + user limit, current = user limit 80%, previous = adaptive. the app's struct has 24 settings bytes but this firmware only sends 4, and mode 2 isn't in the supported mask (probably needs newer firmware)
  • set to 50, 65, 66, and 69 all applied instantly, get reads the new value back
  • above the limit it drains to it even plugged in (Discharging ~6 W with ADP1 online), it doesn't just stop charging
  • a bit under the limit it holds (Not charging, 0 mW). at 64% it held with the limit at 65 and 66, and started charging (~30 W) at 69, so it starts charging again somewhere 3–5% under the limit
  • i didn't try anything outside 50–80 ¯\_(ツ)_/¯

@Cloveian

Copy link
Copy Markdown

fun fact, it rejects anything above 80% or below 50% ( ¬⩊¬)

@petm5

petm5 commented Oct 20, 2026

Copy link
Copy Markdown
Author

@Cloveian Thanks for your findings! I reversed the same commands from SurfaceBatteryClient.sys and the Surface app's BatteryDm.dll, but those commands don't seem to work on my Surface Pro 9.

The Surface app only shows a button to disable adaptive charging temporarily, with no manual control over charge limits. It checks some kind of condition to see what charge limit modes the EC supports, but I don't have the exact details in my notes. I think it had something to do with the return value of battery command 0x3a called Get_Protection_Policy, which returns 0x10 on my device.

The command sudo ./ctrl.py request 0x02 0x01 0x09 0x01 1 (from linux-surface/surface-aggregator-module) should report the user charge mode, but it just fails with a timeout for me. sudo ./ctrl.py request 0x02 0x01 0x0a 0x01 0 0x01 0x50 0x00 0x00 0x00 should limit the charge to 80% but it doesn't seem to have any effect.

@Cloveian

Copy link
Copy Markdown

ahhh, i am on a SP11 (intel) so its probably a little different

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Surface Pro 9 charging stopped working after Ubuntu 24.04 updates (June 3, 2025)

2 participants