Skip to content

Instantly share code, notes, and snippets.

@andmarios
Created April 14, 2019 23:10
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andmarios/a3165cf01d56ceb9233662e44f99307f to your computer and use it in GitHub Desktop.
Save andmarios/a3165cf01d56ceb9233662e44f99307f to your computer and use it in GitHub Desktop.
Script to control battery charge and fn lock for Matebook X Pro. Requires vim (xxd), hexdump, dd, ec_sys module and bash.
#!/usr/bin/env bash
help() {
cat <<EOF
Options:
- cc-disable
Disable charging control
- cc-home
Charging control to Home mode (40%-70%)
- cc-work
Charging control to Work mode (70%-90%)
- cc-travel
Charging control to Travel mode (95%)
- fn-keys
Set default FN (LED off) to F-keys
- fn-functions
Set default FN (LED off) to Functions
- ec
Try to load ec_sys module and enable write support for it
It is required for any other option to work
EOF
}
load_ec_write() {
if modprobe ec_sys write_support=1; then
return
fi
if echo Y > /sys/module/ec_sys/parameters/write_support; then
return
fi
echo "Failed to load ec_sys or enable write_support."
exit 1
}
battery_charge_control_disable() {
# Load byte-register for charge control:
CHARGE_CONTROL_BYTE_REGISTER=$(dd if=/sys/kernel/debug/ec/ec0/io bs=1 count=1 skip=67 conv=notrunc 2>/dev/null | hexdump -e '"%02x"')
if [[ ${#CHARGE_CONTROL_BYTE_REGISTER} -lt 1 || ${#CHARGE_CONTROL_BYTE_REGISTER} -gt 2 ]]; then
echo "Something went wrong when reading the register that holds the charge control status."
# echo "Content: $CHARGE_CONTROL_BYTE_REGISTER. Length: ${#CHARGE_CONTROL_BYTE_REGISTER}."
exit 1
fi
# We need to zero the second Most Significant Bit
CCBR_VALUE=$(( 0x$CHARGE_CONTROL_BYTE_REGISTER & 2#10111111 ))
# Write the new value back to the register
CCBR_VALUE_HEX=$(printf "%02x" $CCBR_VALUE)
echo -n "0000 $CCBR_VALUE_HEX" \
| xxd -r \
| dd of=/sys/kernel/debug/ec/ec0/io bs=1 count=1 seek=67 conv=notrunc
# Write 0s to threshold registers
printf '\x00\x00' \
| dd of=/sys/kernel/debug/ec/ec0/io bs=2 count=1 seek=114 conv=notrunc
}
battery_charge_control_enable() {
LOW_THRESH=$1
HIGH_THRESH=$2
if [[ $LOW_THRESH -lt 40 ||
$LOW_THRESH -gt 95 ||
$HIGH_THRESH -lt 40 ||
$HIGH_THRESH -gt 95 ||
$LOW_THRESH -gt $HIGH_THRESH ]]; then
cat <<EOF
Wrong thresholds. You should set:
40 <= low threshold <= 95
40 <= high threshold <= 95
low threshold <= high threshold
EOF
exit 1
fi
# Set limits:
LOW_THRESH_HEX=$(printf "%02x" $LOW_THRESH)
HIGH_THRESH_HEX=$(printf "%02x" $HIGH_THRESH)
echo "0000 $LOW_THRESH_HEX $HIGH_THRESH_HEX" \
| xxd -r \
| dd of=/sys/kernel/debug/ec/ec0/io bs=2 count=1 seek=114 conv=notrunc
# Load byte-register for charge control:
CHARGE_CONTROL_BYTE_REGISTER=$(dd if=/sys/kernel/debug/ec/ec0/io bs=1 count=1 skip=67 conv=notrunc | hexdump -e '"%02x"')
if [[ ${#CHARGE_CONTROL_BYTE_REGISTER} -lt 1 || ${#CHARGE_CONTROL_BYTE_REGISTER} -gt 2 ]]; then
echo "Something went wrong when reading the register that holds the charge control status."
exit 1
fi
# We need to raise the second Most Significant Bit
CCBR_VALUE=$(( 0x$CHARGE_CONTROL_BYTE_REGISTER | 2#01000000 ))
# Write the new value back to the register
CCBR_VALUE_HEX=$(printf "%02x" $CCBR_VALUE)
echo "0000 $CCBR_VALUE_HEX" \
| xxd -r \
| dd of=/sys/kernel/debug/ec/ec0/io bs=1 count=1 seek=67 conv=notrunc
}
fn_lock() {
BW_OP=""
case $1 in
keys)
BW_OP=" | 2#00000001"
;;
functions)
BW_OP=" & 2#11111110"
;;
*)
echo "Wrong toggle."
exit 1
;;
esac
# Load 2 byte-registers for FN lock:
FN_LOCK_BYTE_REGISTER_1=$(dd if=/sys/kernel/debug/ec/ec0/io bs=1 count=1 skip=230 conv=notrunc | hexdump -e '"%02x"')
FN_LOCK_BYTE_REGISTER_2=$(dd if=/sys/kernel/debug/ec/ec0/io bs=1 count=1 skip=231 conv=notrunc | hexdump -e '"%02x"')
if [[ ${#FN_LOCK_BYTE_REGISTER_1} -lt 1 || ${#FN_LOCK_BYTE_REGISTER_1} -gt 2 ]]; then
echo "Something went wrong when reading the register #1 that holds the FN Lock status."
exit 1
fi
if [[ ${#FN_LOCK_BYTE_REGISTER_2} -lt 1 || ${#FN_LOCK_BYTE_REGISTER_2} -gt 2 ]]; then
echo "Something went wrong when reading the register #2 that holds the FN Lock status."
exit 1
fi
# New values:
REGISTER_1_HEX=$(printf "%02x" $(( 0x$FN_LOCK_BYTE_REGISTER_1 $BW_OP )) )
REGISTER_2_HEX=$(printf "%02x" $(( 0x$FN_LOCK_BYTE_REGISTER_2 $BW_OP )) )
echo "0000 $REGISTER_1_HEX $REGISTER_2_HEX" \
| xxd -r \
| dd of=/sys/kernel/debug/ec/ec0/io bs=2 count=1 seek=115 conv=notrunc
}
case $1 in
cc-disable)
battery_charge_control_disable
;;
cc-home)
battery_charge_control_enable 40 70
;;
cc-work)
battery_charge_control_enable 70 90
;;
cc-travel)
battery_charge_control_enable 95 95
;;
fn-keys)
fn_lock keys
;;
fn-functions)
fn_lock functions
;;
ec)
load_ec_write
;;
*)
help
;;
esac
@luke-jr
Copy link

luke-jr commented Aug 31, 2019

How do you reverse-engineer the sys_ec data?

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