小白都能看懂的DSDT电量显示补丁教程

[TOC]

简介

知识储备

  • DSDT
  • 正则匹配(不懂就依葫芦画瓢)

背景

由于普通PC的电池设备并不兼容与苹果的SMbus设备,所以,对于黑苹果,只能够通过ACPI来获取电池状态。为了解决电量显示我可是爬了不少帖子、花了不少时间呢。但是从我参考的中外教程来看,我觉得都不太理想,作为一个程序员的我都要看好几遍,普通用户怕是望而却步。再说作为小白用户其实没必要搞清楚里面的弯弯道道,能正常显示电量就是目标,所以萌生了写这篇文章的念头。

参考资料

准备阶段

汇集中外教程资料,准备步骤如下

  1. 在DSDT文件中搜索关键词“EmbeddedControl” ,有些机型可能会有多个

    1
    2
    # 定义一个0xFF(255)字节的EC域ECF2
    OperationRegion (ECF2, EmbeddedControl, Zero, 0xFF)
  2. 接着搜关键词“ECF2”(上面EmbeddedControl搜索到多个EC域的都要找),有些机型即使只有一个EC域也在多个块有声明(如例子附件中E430)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Field (ECF2, ByteAcc, Lock, Preserve)
    {
    MBVR, 8,
    Offset (0x10),
    BDN0, 56,
    Offset (0x18),
    BME0, 8,
    Offset (0x1C),
    DAY0, 8,
    HUR0, 8,
    MIN0, 8,
    SEC0, 8,
    BMN0, 128,
    BCT0, 128,
    Offset (0x59),
    EC59, 1,
    DPDS, 1,
    ……
    }
  3. 将第二步中所有Field里大于8位的属性定义且在别的地方有访问的都找出来,把这些变量名及调用的代码都一一用文本文件记录下来,后面高级进阶要用。任何属性在任何位置只是声明没有调用的都不用管

  4. 计算大于32位属性的偏移量,如上述代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 16进制记法:0到15(即0 1 2 3 4 5 6 7 8 9 A B C D E F),满16进位(0x10=16)
    Field (ECF2, ByteAcc, Lock, Preserve)
    {
    MBVR, 8,
    Offset (0x10), // 空10
    BDN0, 56, // 0x10
    Offset (0x18),
    BME0, 8, // 0x18
    Offset (0x1C), //
    DAY0, 8, // 0x1C
    HUR0, 8, // 0x1D=0x1C+1 8位是1字节,所以加1
    MIN0, 8, // 0x1E
    SEC0, 8, // 0x1F
    BMN0, 128, // 0x20
    BCT0, 128, // 0x31=0x20+0x10(16)+1
    Offset (0x59),
    EC59, 1,
    DPDS, 1,
    ……
    }

修改阶段

暴力修改

Laptop-DSDT-Patch下载了battery_HP-Envy-14(我的是HP Envy 13,当时认为应该是最接近的)参考对照,补丁和源文件都有的留下,补丁有源文件没有的删了,再依葫芦画瓢把源文件有补丁没有的补上,最后编译,除了16位、32位拆字节补丁无效(也就是类似下面的代码),其它的都正常,然后就傻不拉几的手工拆

1
2
3
# 我确定自己的设备标识也是H_EC,所以我改成into scope label ^^LPCB
into device label H_EC code_regex BDC0,\s+16 replace_matched begin DC00,8,DC01,8 end;
……

特别注意类型如下代码

1
2
3
4
5
6
# 0x10即上面找出来的BDN0偏移量
into method label GBTI code_regex \(BDN0, replaceall_matched begin (RECB(0x10,56), end;
# 0x20即上面找出来的BMN0偏移量
into method label GBTI code_regex \(BMN0, replaceall_matched begin (RECB(0x20,128), end;
# 0x31即上面找出来的BCT0偏移量
into method label GBTI code_regex \(BCT0, replaceall_matched begin (RECB(0x31,128), end;

编译成功另存为aml文件替换重启正常显示电量。至此,如果你不想继续看,用我一样的笨方法理论上是能解决问题的。码农天生喜欢折腾,不然也不会玩黑苹果了,于是我把补丁文件研究了一番寻求优雅方式

高级进阶

通过分析补丁文件,我发现了一个特点,就是除了增加方法,其它的都是正则匹配替换(在这里我假设你懂正则),于是我去看了Patching Syntax Grammar,下面我简单说明一下

  • 第一部分into|into_all就是在什么范围(后面跟着的限定),into应该的匹配第一个,into_all有多少匹配多少

  • 第二部分即限定,可以有All|DefinitionBlock|Scope|Method|Device|Processor|ThermalZone这些值

  • 第三部分即名称标识啥的了,支持标签名称、正则匹配、父项名称等

  • 第四部分selector,即选择器(支持正则)

  • 第五部分动作(就是在匹配项里干什么),支持

    insert|set_label|replace_matched|replaceall_matched|remove_matched|removeall_matched|remove_entry|replacecontent|store%8|store_%9这些

  • begin end里面的就是主体

拆字节补丁例子

1
2
3
4
5
6
7
8
9
10
# 在范围为^LPCB中查找(即)BDC0, 16的内容(s+就是有至少一个空格),替换成DC00,8,DC01,8
# 也就是将BDC0, 16拆成两个字节DC00,8,DC01,8
# 注意这里16后面没有逗号(,),替换第二个8后面也没有,所以即使是}前一个也是通用的
# 这个是GitHub下载补丁里的,我的设备标识是H_EC,但我发现无效,原因未知,于是我改了

into device label H_EC code_regex BDC0,\s+16 replace_matched begin DC00,8,DC01,8 end;
into scope label ^^LPCB code_regex BDC0,\s+16 replace_matched begin DC00,8,DC01,8 end;
# 拆分32位属性
into scope code_regex BTY0,\s+32 replace_matched begin TY00,8,TY01,8,TY02,8,TY03,8 end;
# 所有大于32位的都不用拆

DSDT-Patch-1.png

从上面的图片可以看到补丁完美实现拆字节,下面再看看调用例子

访问属性补丁例子

1
2
3
4
5
6
7
8
9
10
11
12
# 16位属性访问替换补丁
# 注意BFC0在两个方法中有调用,都要写补丁替换
into method label BTIF code_regex \(BFC0, replaceall_matched begin (B1B2(FC00,FC01), end;
into method label _Q09 code_regex \^\^BFC0\) replaceall_matched begin ^^B1B2(FC00,FC01)) end;
# 32位属性访问替换补丁
into method label GBTI code_regex \(BTY0, replaceall_matched begin (B1B4(TY00,TY01,TY02,TY03), end;
# 56位属性替换补丁
into method label GBTI code_regex \(BDN0, replaceall_matched begin (RECB(0x10,56), end;
# 128位属性替换补丁
into method label GBTI code_regex \(BMN0, replaceall_matched begin (RECB(0x20,128), end;
# 从56位和128位属性访问补丁可以看到是通过RECB(偏移量, 位数)这个方法实现的,理论上16位、32位的
# 也可以不拆,访问的时候用这个方法,但是回到准备阶段第4部我们发现算每个属性的偏移量这工作比拆字节还头大

DSDT-Patch-2.png

其它举例

1
2
3
4
# 在做Thinakpad E430电量补丁的时候发现有类型\_SB.PCI0.LPCB.EC.HWAK这样的调用,我用下面的补丁修正
into method label _L43 code_regex \(\\\_SB.PCI0.LPCB.EC.HWAK replaceall_matched begin \(\\_SB.B1B2(\\_SB.PCI0.LPCB.EC.AK00, \\_SB.PCI0.LPCB.EC.AK01) end;
# 理论上不需要上面那个补丁应该都能替换掉所有的,但发现不彻底,原因未知
into ALL code_regex \(\\\_SB.PCI0.LPCB.EC.HWAK replaceall_matched begin \(\\_SB.B1B2(\\_SB.PCI0.LPCB.EC.AK00, \\_SB.PCI0.LPCB.EC.AK01) end;

RehabMan大神提供通用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# 这部分之间拷贝到你的补丁文件前面
# 特别注意部分行后面的\n,因为补丁里你即使输入了换行也是当一行出来的,所以加入了硬换行代码
# 删除原有B1B2方法(一般都没)
into method label B1B2 remove_entry;
# 新插入B1B2方法
# 这里需要注意一下,一般情况下默认的都没问题
# 有些型号可能会导致打完补丁的少量代码访问不了B1B2方法
# 这时候要么调整补丁绝对路径访问,要么插入的时候改个位置
into definitionblock code_regex . insert
# into Scope label \_SB code_regex . insert // 这是我改的位置
begin
Method (B1B2, 2, NotSerialized) { Return(Or(Arg0, ShiftLeft(Arg1, 8))) }
end;

into method label B1B4 remove_entry;
into definitionblock code_regex . insert
begin
Method (B1B4, 4, NotSerialized)\n
{\n
Store(Arg3, Local0)\n
Or(Arg2, ShiftLeft(Local0, 8), Local0)\n
Or(Arg1, ShiftLeft(Local0, 8), Local0)\n
Or(Arg0, ShiftLeft(Local0, 8), Local0)\n
Return(Local0)\n
}\n
end;

into method label RE1B parent_label EC0 remove_entry;
into method label RECB parent_label EC0 remove_entry;
into device label EC0 insert
begin\n
Method (RE1B, 1, NotSerialized)\n
// Arg0 - offset in bytes from zero-based EC\n
{\n
OperationRegion(ECOR, EmbeddedControl, Arg0, 1)\n
Field(ECOR, ByteAcc, NoLock, Preserve) { BYTE, 8 }\n
Return(BYTE)\n
}\n
Method (RECB, 2, Serialized)\n
// Arg0 - offset in bytes from zero-based EC\n
// Arg1 - size of buffer in bits\n
{\n
ShiftRight(Arg1, 3, Arg1)\n
Name(TEMP, Buffer(Arg1) { })\n
Add(Arg0, Arg1, Arg1)\n
Store(0, Local0)\n
While (LLess(Arg0, Arg1))\n
{\n
Store(RE1B(Arg0), Index(TEMP, Local0))
Increment(Arg0)
Increment(Local0)
}
Return(TEMP)\n
}\n
end;

into method label RE1B parent_label EC remove_entry;
into method label RECB parent_label EC remove_entry;
into device label EC insert
begin
Method (RE1B, 1, NotSerialized)\n
// Arg0 - offset in bytes from zero-based EC\n
{\n
OperationRegion(ECOR, EmbeddedControl, Arg0, 1)\n
Field(ECOR, ByteAcc, NoLock, Preserve) { BYTE, 8 }\n
Return(BYTE)\n
}\n
Method (RECB, 2, Serialized)\n
// Arg0 - offset in bytes from zero-based EC\n
// Arg1 - size of buffer in bits\n
{\n
ShiftRight(Arg1, 3, Arg1)\n
Name(TEMP, Buffer(Arg1) { })\n
Add(Arg0, Arg1, Arg1)\n
Store(0, Local0)\n
While (LLess(Arg0, Arg1))\n
{\n
Store(RE1B(Arg0), Index(TEMP, Local0))\n
Increment(Arg0)\n
Increment(Local0)\n
}\n
Return(TEMP)\n
}\n
end;

补丁例子

提供我做的两个型号(HP-ENVY-13及ThinkaPad E430)的补丁及原始DSL文件参考

链接: https://pan.baidu.com/s/1Y8Vcnzn-SFTEZ_itMcRvKw 密码: gryy

-------------本文结束感谢您的阅读-------------