Contribute
Register

[Guide] How to patch DSDT for working battery status

RehabMan

Moderator
Joined
May 3, 2012
Messages
191,731
Motherboard
Intel DH67BL
CPU
Core i7-2600K
Graphics
Intel HD 3000
Mac
MacBook Air
Mobile Phone
iOS
Background

Because the battery hardware in PCs is not compatible with Apple SMbus hardware, we use ACPI to access battery state when running OS X on laptops. Generally I recommend you use ACPIBatteryManager.kext, available here: https://github.com/RehabMan/OS-X-ACPI-Battery-Driver

Later releases of AppleACPIPlatform are unable to correctly access fields within the EC (embedded controller). This causes problems for ACPIBatteryManager as the various ACPI methods for battery fail (_BIF, _STA, _BST, etc). Although it is possible to use an older version of AppleACPIPlatform (from Snow Leopard), it is desirable to use the latest version of AppleACPIPlatform because with computers that have Ivy Bridge CPUs it enables native power management for those computers. To use the latest version, DSDT must be changed to comply with the limitations of Apple's AppleACPIPlatform.

In particular, any fields in the EC larger than 8-bit, must be changed to be accessed 8-bits at one time. This includes 16, 32, 64, and larger fields.

You should be familiar with the DSDT/SSDT patching fundamentals: http://www.tonymacx86.com/yosemite-laptop-support/152573-guide-patching-laptop-dsdt-ssdts.html

Existing Patches

First of all, it could be that there is patch already available for your laptop. See my patches at: https://github.com/RehabMan/Laptop-DSDT-Patch

In order to match your DSDT with a patch, it is often necessary to understand how the patches are made in the first place, so you know what to look for in your DSDT and can match what you see with the patches already available. A patch set that has a high ratio of changes to patches, creates no errors, and appears to patch all fields that need to be patched is likely a match.

More info here: https://www.tonymacx86.com/threads/guide-how-to-patch-dsdt-for-working-battery-status.116102/page-333#post-1360697

*** Note: Do not use DSDT Editor or any program other than MaciASL. I do not test my patches with DSDT Editor. I test only with MaciASL.

Other relevant DSDT patches

In addition to the multi-byte EC fields, there are a few other DSDT issues that can affect battery status. These particular problems are not specific to battery status, but they are usually noticed for the first time when trying to implement battery status.

The battery code may depend on having a recognized version of Windows as the host OS. To fix, apply "OS Check Fix" from the laptop DSDT patch repository. This will cause the DSDT to take the same actions as it would when running "Windows 2006" You can change the patch to effect different selections (eg. "Windows 2012".

Another common problem is the fact that OS X's ACPI implementation has difficulty with Mutex objects declared with a non-zero SyncLevel (for more info read the ACPI spec). To fix, apply "Fix Mutex with non-zero SyncLevel" from the laptop DSDT patch repository.


Skills Required

DSDT is a "program." As such, it is helpful to have some programming/computer skills when modifying it. Also, DSDT patches themselves have their own language (described briefly in the MaciASL wiki, available at here: http://sourceforge.net/projects/maciasl/). Finally, the patches themselves are basically scoped regular expression search/replace, so it is helpful to understand regular expressions (regex). Familiarity with compilers, compiler errors, and an ability to determine what the compiler is telling you about errors in the code is also useful.

Also, it is a good idea to be familiar with ACPI. You can download the specification here: https://www.acpica.org/

It is not the purpose of this guide to teach you basic programming skills, regular expressions, or the ACPI language.

The patching process

I use a rather 'mechanical' process to patching DSDT for battery status. I simply look for the parts that OS X finds offensive and mechanically convert it. I don't try too hard to determine what sections of the code are actually going to execute, I just convert everything that I see.

To follow along, download the example DSDT from this post and follow along. This particular example DSDT is for an HP Envy 14. The final, complete patch, is available from my patch repo as "HP Envy 14."

First start by identifing the areas of the DSDT that are likely to need changes. Load the DSDT into MaciASL and search for 'EmbeddedControl'. There can be several 'EmbeddedControl' sections in a single DSDT, each with field declarations attached to it.

So, I always start out looking for 'embeddedcontrol' in order to find this declaration.

In the example DSDT, you will find this single EC region:
Code:
   OperationRegion (ECF2, EmbeddedControl, Zero, 0xFF)
The above code defines a 255 byte EC region.

We know it is called ECF2, so now we want to search for 'Field (ECF2'. As you can see in the example DSDT, there is only one Field definition referring to this region. Sometimes there are many.

The Field definition describes a breakdown of that 255 byte EC region above. You can tell it is related because the name ECF2 is referred to by the Field. Think of this as a structure (struct for C programmers) into the EC.

The next step is to examine the items in the Field definition, looking for items which are larger than 8-bit. For example, the first field declared is BDN0, 56:
Code:
   Field (ECF2, ByteAcc, Lock, Preserve)
   {
       Offset (0x10),
       BDN0,   56,
...
It is a 56-bit field. Larger than 8-bit and if it is accessed in the DSDT, the code there will need edits as will the definition of this field. In the example DSDT, if you search the rest of the DSDT for "BDN0", you will find:

Code:
   Store (BDN0, BDN)
This is intended to store the value at BDN0 (in the EC) into BDN. When fields larger than 32-bit are accessed, they are accessed as type Buffer. Fields 32-bit or under are accessed as Integers. This is important to realize as you change the code. Buffers are a bit more work to change. Also, realize this code is "reading" from the EC. Reads and writes must be handled differently.

So, for this particular line of code, the goal is to read this 56-bit field (7 bytes) 8-bits at time into a buffer, so the resulting buffer can be stored into BDN. We will get back to how this is accomplished later, for now let's explore the rest of the fields in this EC.

Looking through the rest of the the EC, we look for all fields larger than 8-bit, and for each one, search the rest of the DSDT to see if they are accessed. It is common that some fields are not accessed and for those we don't have to do anything. So, the next field we see is BMN0:
Code:
   BMN0,   32,
If we search the DSDT for 'BMN0' we find only this declaration, so it is not accessed. We can ignore this one. Same with BMN4. BCT0, on the other hand, is 128 bit and is accessed, much like BDN0:
Code:
   Store (BCT0, CTN)
Further searching leads to this comprehensive list:
Code:
                        BDN0,   56,
                        BCT0,   128,
                        BDN1,   56,
                        BCT1,   128,
...
                        BDC0,   16,
                        BDC1,   16,
                        BFC0,   16,
                        BFC1,   16,
...
                        BDV0,   16,
                        BDV1,   16,
...
                        BPR0,   16,
                        BPR1,   16,

                        BRC0,   16,
                        BRC1,   16,
                        BCC0,   16,
                        BCC1,   16,
                        CV01,   16,
                        CV02,   16,
                        CV03,   16,
                        CV04,   16,
                        CV11,   16,
                        CV12,   16,
                        CV13,   16,
                        CV14,   16,
...
                        BMD0,   16,
                        BMD1,   16,
                        BPV0,   16,
                        BPV1,   16,
                        BSN0,   16,
                        BSN1,   16,
                        BCV0,   16,
                        BCV1,   16,
                        CRG0,   16,
                        CRG1,   16,
                        BTY0,   32,
                        BTY1,   32,
...
                        CBT0,   16,
                        CBT1,   16,
As you can see, there are quite a few fields in this DSDT that need work, and all of various sizes. 16-bit, 32-bit, 56-bit and 128-bit.

Fields sized 16-bit and 32-bit

Fields that are 16-bit and 32-bit are the easiest to deal with, so let's start there. Let's take for example, the first 16-bit field in the list above, BDC0. What we want to do is change this field so it is broken into two peices (low-byte, high-byte). To do that we need to come up with a 4-character name that does not conflict with any other names in the DSDT. It is often easy to remove the first letter and use the following three.
Code:
   // was: BDC0, 16
   DC00, 8,
   DC01, 8,
A patch for this would look like:
Code:
into device label H_EC code_regex BDC0,\s+16, replace_matched begin DC00,8,DC01,8, end;
That patch says, look at the code in a device with label H_EC, search for "BDC0,<spaces>16," and replace it with "DC00,8,DC01,8," This effectively breaks the field into two parts. If you apply this patch, and attempt to compile the modified DSDT, you will get errors because the code is still accessing BDC0. These errors actually help us identify what code needs to change:
Code:
   Store (BDC0, Index (DerefOf (Index (Local0, 0x02)), Zero))
   Store (ShiftRight (BDC0, 0x08), Index (DerefOf (Index (Local0, 0x02)), One))
As you can see this code is reading from BDC0, which is now in two parts. To make a patch easier to construct, we use a utility method called B1B2, which we insert into DSDT with this patch:
Code:
into method label B1B2 remove_entry;
into definitionblock code_regex . insert
begin
Method (B1B2, 2, NotSerialized) { Return(Or(Arg0, ShiftLeft(Arg1, 8))) }\n
end;
This method takes two arguments (low-byte, high-byte), and returns a 16-bit value from the two 8-bit values.

For the code above, we want to translate it to:
Code:
   Store (B1B2(DC00,DC01), Index (DerefOf (Index (Local0, 0x02)), Zero))
   Store (ShiftRight (B1B2(DC00,DC01), 0x08), Index (DerefOf (Index (Local0, 0x02)), One))
A patch can be constructed to automate this, and as you will see patches for other 16-bit fields follow this same pattern:
Code:
into method label GBTI code_regex \(BDC0, replaceall_matched begin (B1B2(DC00,DC01), end;
An astute reader will note that this code could be optimized:
Code:
   Store (DC00, Index (DerefOf (Index (Local0, 0x02)), Zero))
   Store (DC01, Index (DerefOf (Index (Local0, 0x02)), One))
Such optimizations can only be made by using your brain, and generally it is not worth it. The goal here is to come up with an automated method of fixing this code and not to attempt to use our brain too much as we could introduce bugs into the code if we change it too drastically. Also, this kind of code is rare (I have only seen it only in two DSDTs out of more than 20 that I've written patches for).

Now that you understand how to deal with 16-bit registers, it is probably easiest to just convert all of them. Here is the comprehensive patch for the 16-bit EC fields:
Code:
# 16-bit registers
into device label H_EC code_regex BDC0,\s+16 replace_matched begin DC00,8,DC01,8 end;
into device label H_EC code_regex BDC1,\s+16 replace_matched begin DC10,8,DC11,8 end;
into device label H_EC code_regex BFC0,\s+16 replace_matched begin FC00,8,FC01,8 end;
into device label H_EC code_regex BFC1,\s+16 replace_matched begin FC10,8,FC11,8 end;
into device label H_EC code_regex BDV0,\s+16 replace_matched begin DV00,8,DV01,8 end;
into device label H_EC code_regex BDV1,\s+16 replace_matched begin DV10,8,DV11,8 end;
into device label H_EC code_regex BPR0,\s+16 replace_matched begin PR00,8,PR01,8 end;
into device label H_EC code_regex BPR1,\s+16 replace_matched begin PR10,8,PR11,8 end;
into device label H_EC code_regex BRC0,\s+16 replace_matched begin RC00,8,RC01,8 end;
into device label H_EC code_regex BRC1,\s+16 replace_matched begin RC10,8,RC11,8 end;
into device label H_EC code_regex BCC0,\s+16 replace_matched begin CC00,8,CC01,8 end;
into device label H_EC code_regex BCC1,\s+16 replace_matched begin CC10,8,CC11,8 end;
into device label H_EC code_regex CV01,\s+16 replace_matched begin CV10,8,CV11,8 end;
into device label H_EC code_regex CV02,\s+16 replace_matched begin CV20,8,CV21,8 end;
into device label H_EC code_regex CV03,\s+16 replace_matched begin CV30,8,CV31,8 end;
into device label H_EC code_regex CV04,\s+16 replace_matched begin CV40,8,CV41,8 end;
into device label H_EC code_regex CV11,\s+16 replace_matched begin CV50,8,CV51,8 end;
into device label H_EC code_regex CV12,\s+16 replace_matched begin CV60,8,CV61,8 end;
into device label H_EC code_regex CV13,\s+16 replace_matched begin CV70,8,CV71,8 end;
into device label H_EC code_regex CV14,\s+16 replace_matched begin CV80,8,CV81,8 end;
into device label H_EC code_regex HPBA,\s+16 replace_matched begin PBA0,8,PBA1,8 end;
into device label H_EC code_regex HPBB,\s+16 replace_matched begin PBB0,8,PBB1,8 end;
into device label H_EC code_regex BMD0,\s+16 replace_matched begin MD00,8,MD01,8 end;
into device label H_EC code_regex BMD1,\s+16 replace_matched begin MD10,8,MD11,8 end;
into device label H_EC code_regex BPV0,\s+16 replace_matched begin PV00,8,PV01,8 end;
into device label H_EC code_regex BPV1,\s+16 replace_matched begin PV10,8,PV11,8 end;
into device label H_EC code_regex BSN0,\s+16 replace_matched begin SN00,8,SN01,8 end;
into device label H_EC code_regex BSN1,\s+16 replace_matched begin SN10,8,SN11,8 end;
into device label H_EC code_regex BCV0,\s+16 replace_matched begin BV00,8,BV01,8 end;
into device label H_EC code_regex BCV1,\s+16 replace_matched begin BV10,8,BV11,8 end;
into device label H_EC code_regex CRG0,\s+16 replace_matched begin RG00,8,RG01,8 end;
into device label H_EC code_regex CRG1,\s+16 replace_matched begin RG10,8,RG11,8 end;
into device label H_EC code_regex CBT0,\s+16 replace_matched begin BT00,8,BT01,8 end;
into device label H_EC code_regex CBT1,\s+16 replace_matched begin BT10,8,BT11,8 end;
And all the code which accesses those registers needs to be fixed:
Code:
# fix 16-bit methods
into method label GBTI code_regex \(BDC0, replaceall_matched begin (B1B2(DC00,DC01), end;
into method label GBTI code_regex \(BDC1, replaceall_matched begin (B1B2(DC10,DC11), end;
into method label GBTI code_regex \(BFC0, replaceall_matched begin (B1B2(FC00,FC01), end;
into method label GBTI code_regex \(BFC1, replaceall_matched begin (B1B2(FC10,FC11), end;
into method label BTIF code_regex \(BFC0, replaceall_matched begin (B1B2(FC00,FC01), end;
into method label BTIF code_regex \(BFC1, replaceall_matched begin (B1B2(FC10,FC11), end;
into method label ITLB code_regex \(BFC1, replaceall_matched begin (B1B2(FC10,FC11), end;
into method label ITLB code_regex \sBFC0, replaceall_matched begin B1B2(FC00,FC01), end;
into method label _Q09 code_regex \(BRC0, replaceall_matched begin (B1B2(RC00,RC01), end;
into method label _Q09 code_regex \sBFC0\) replaceall_matched begin B1B2(FC00,FC01)) end;
into method label GBTI code_regex \(BDV0, replaceall_matched begin (B1B2(DV00,DV01), end;
into method label GBTI code_regex \(BDV1, replaceall_matched begin (B1B2(DV10,DV11), end;
into method label BTIF code_regex \(BDV0, replaceall_matched begin (B1B2(DV00,DV01), end;
into method label BTIF code_regex \(BDV1, replaceall_matched begin (B1B2(DV10,DV11), end;
into method label GBTI code_regex \(BPR0, replaceall_matched begin (B1B2(PR00,PR01), end;
into method label GBTI code_regex \(BPR1, replaceall_matched begin (B1B2(PR10,PR11), end;
into method label BTST code_regex \sBPR0, replaceall_matched begin B1B2(PR00,PR01), end;
into method label BTST code_regex \sBPR1, replaceall_matched begin B1B2(PR10,PR11), end;
into method label BTST code_regex \(BPR0, replaceall_matched begin (B1B2(PR00,PR01), end;
into method label BTST code_regex \(BPR1, replaceall_matched begin (B1B2(PR10,PR11), end;
into method label BTST code_regex \(BRC0, replaceall_matched begin (B1B2(RC00,RC01), end;
into method label BTST code_regex \(BRC1, replaceall_matched begin (B1B2(RC10,RC11), end;
into method label GBTI code_regex \(BRC0, replaceall_matched begin (B1B2(RC00,RC01), end;
into method label GBTI code_regex \(BRC1, replaceall_matched begin (B1B2(RC10,RC11), end;
into method label _Q09 code_regex \(BRC0, replaceall_matched begin (B1B2(RC00,RC01), end;
into method label GBTI code_regex \(BCC0, replaceall_matched begin (B1B2(CC00,CC01), end;
into method label GBTI code_regex \(BCC1, replaceall_matched begin (B1B2(CC10,CC11), end;
into method label GBTI code_regex \(CV01, replaceall_matched begin (B1B2(CV10,CV11), end;
into method label GBTI code_regex \(CV02, replaceall_matched begin (B1B2(CV20,CV21), end;
into method label GBTI code_regex \(CV03, replaceall_matched begin (B1B2(CV30,CV31), end;
into method label GBTI code_regex \(CV04, replaceall_matched begin (B1B2(CV40,CV41), end;
into method label GBTI code_regex \(CV11, replaceall_matched begin (B1B2(CV50,CV51), end;
into method label GBTI code_regex \(CV12, replaceall_matched begin (B1B2(CV60,CV61), end;
into method label GBTI code_regex \(CV13, replaceall_matched begin (B1B2(CV70,CV71), end;
into method label GBTI code_regex \(CV14, replaceall_matched begin (B1B2(CV80,CV81), end;
into method label BTIF code_regex \(BMD0, replaceall_matched begin (B1B2(MD00,MD01), end;
into method label BTIF code_regex \(BMD1, replaceall_matched begin (B1B2(MD10,MD11), end;
into method label GBTI code_regex \sBMD0\) replaceall_matched begin B1B2(MD00,MD01)) end;
into method label GBTI code_regex \(BMD0, replaceall_matched begin (B1B2(MD00,MD01), end;
into method label GBTI code_regex \sBMD1\) replaceall_matched begin B1B2(MD10,MD11)) end;
into method label GBTI code_regex \(BMD1, replaceall_matched begin (B1B2(MD10,MD11), end;
into method label BTST code_regex \(BPV0, replaceall_matched begin (B1B2(PV00,PV01), end;
into method label BTST code_regex \(BPV1, replaceall_matched begin (B1B2(PV10,PV11), end;
into method label GBTI code_regex \(BPV0, replaceall_matched begin (B1B2(PV00,PV01), end;
into method label GBTI code_regex \(BPV1, replaceall_matched begin (B1B2(PV10,PV11), end;
into method label BTIF code_regex \(BSN0, replaceall_matched begin (B1B2(SN00,SN01), end;
into method label BTIF code_regex \(BSN1, replaceall_matched begin (B1B2(SN10,SN11), end;
into method label GBTI code_regex \(BSN0, replaceall_matched begin (B1B2(SN00,SN01), end;
into method label GBTI code_regex \(BSN1, replaceall_matched begin (B1B2(SN10,SN11), end;
into method label GBTI code_regex \(BCV0, replaceall_matched begin (B1B2(BV00,BV01), end;
into method label GBTI code_regex \(BCV1, replaceall_matched begin (B1B2(BV10,BV11), end;
into method label GBTI code_regex \(CRG0, replaceall_matched begin (B1B2(RG00,RG01), end;
into method label GBTI code_regex \(CRG1, replaceall_matched begin (B1B2(RG10,RG11), end;
into method label GBTI code_regex \(CBT0, replaceall_matched begin (B1B2(BT00,BT01), end;
into method label GBTI code_regex \(CBT1, replaceall_matched begin (B1B2(BT10,BT11), end;

Now, what about the 32-bit registers, BTY0, and BTY1? These are handled similarly to 16-bit, except we need a B1B4 method that can construct a 32-bit value out of four 8-bit values:
Code:
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;
And we need to convert BTY0 and BTY1 into four 8-bit registers:
Code:
# 32-bit registers
into device label H_EC code_regex BTY0,\s+32 replace_matched begin TY00,8,TY01,8,TY02,8,TY03,8 end;
into device label H_EC code_regex BTY1,\s+32 replace_matched begin TY10,8,TY11,8,TY12,8,TY13,8 end;
And the code in GBTI needs fixing:
Code:
   Store (BTY0, BTY)
...
   Store (BTY1, BTYB)
Similar to the patches for 16-bit, but instead we use B1B4:
Code:
# fix 32-bit methods
into method label GBTI code_regex \(BTY0, replaceall_matched begin (B1B4(TY00,TY01,TY02,TY03), end;
into method label GBTI code_regex \(BTY1, replaceall_matched begin (B1B4(TY10,TY11,TY12,TY13), end;
Which changes the above code, as follows:
Code:
   Store (B1B4(TY00,TY01,TY02,TY03), BTY)
...
   Store (B1B4(TY10,TY11,TY12,TY13), BTYB)

Buffer Fields (fields larger than 32-bit

Back to our original search for fields larger than 8-bit, we had these fields larger than 32-bit:
Code:
   BDN0,   56,
   BCT0,   128,
   BDN1,   56,
   BCT1,   128,
To access these fields 8-bit at a time is rather tedious, so I like to access them by offset, and to make sure no existing code accesses them directly, we rename them:
Code:
into device label H_EC code_regex (BDN0,)\s+(56) replace_matched begin BDNX,%2,//%1%2 end;
into device label H_EC code_regex (BDN1,)\s+(56) replace_matched begin BDNY,%2,//%1%2 end;
into device label H_EC code_regex (BCT0,)\s+(128) replace_matched begin BCTX,%2,//%1%2 end;
into device label H_EC code_regex (BCT1,)\s+(128) replace_matched begin BCTY,%2,//%1%2 end;
Next we need to determine the offsets within the EC that these fields are placed. Keep in mind the sizes are in bits, but the offsets are in bytes. The offsets I have in the comments below are in hex. See if you can come up with the same numbers.

Code:
   Field (ECF2, ByteAcc, Lock, Preserve)
   {
            Offset (0x10),
       BDN0,   56,     //!!0x10
            Offset (0x18),
       BME0,   8,
            Offset (0x20),
       BMN0,   32,     //0x20
       BMN2,   8,     //0x24
       BMN4,   88,    //0x25
       BCT0,   128,     //!! 0x30
       BDN1,   56,     //!! 0x40
            Offset (0x48),
       BME1,   8,
            Offset (0x50),
       BMN1,   32,     //0x50
       BMN3,   8,     //0x54
       BMN5,   88,     //0x55
       BCT1,   128,     //!!0x60
Once you run the patch for renaming, and then compile, the compiler will tell you what code needs attention. In our case, we see this code with errors:
Code:
...
   Store (BCT0, CTN)
...
   Store (BDN0, BDN)
...
   Store (BCT1, CTNB)
...
   Store (BDN1, BDNB)
...
There are errors because we renamed them from BCT0, BDN0, BCT1, BDN1 to BCTX, BDNX, BCTY, BDNY, respectively.

As you can see this code is reading from these EC buffer fields and storing them somewhere else. In order to read these items 8-bits at at time, we need a utility method:
Code:
# utility methods to read/write buffers from/to EC
into method label RE1B parent_label H_EC remove_entry;
into method label RECB parent_label H_EC remove_entry;
into device label H_EC insert
begin
Method (RE1B, 1, NotSerialized)\n
{\n
    OperationRegion(ERAM, EmbeddedControl, Arg0, 1)\n
    Field(ERAM, 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(Add(Arg1,7), 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;
"RECB" stands for "Read EC Buffer". It takes two parameters indicating the offset within the EC and the size in bits of the field you wish to read. The size in bits must be a multiple of eight (8). The code does not check.

These helper methods must be defined in the EC device, in the case of this DSDT, named H_EC:
Code:
                Device (H_EC)
                {
                    Name (_HID, EisaId ("PNP0C09"))
You will need to change the patches that create RECB/RE1B if the name of your EC device is different. Common names are EC, EC0, and as in this example H_EC.

To handle the first case of BCT0, we want to do the equivalent of this:
Code:
   Store(RECB(0x30,128), CTN)
The 0x30 is the offset of the BTC0 field (now called BCTX) and the 128 is the number of bits.

These edits can be accomplished with these patches:
Code:
into method label GBTI code_regex \(BCT0, replaceall_matched begin (RECB(0x30,128), end;
into method label GBTI code_regex \(BCT1, replaceall_matched begin (RECB(0x60,128), end;
into method label GBTI code_regex \(BDN0, replaceall_matched begin (RECB(0x10,56), end;
into method label GBTI code_regex \(BDN1, replaceall_matched begin (RECB(0x40,56), end;
This DSDT does not have any writes to EC buffers, but if it did, this utility method is useful:
Code:
into method label WE1B parent_label H_EC remove_entry;
into method label WECB parent_label H_EC remove_entry;
into device label H_EC insert
begin
Method (WE1B, 2, NotSerialized)\n
{\n
    OperationRegion(ERAM, EmbeddedControl, Arg0, 1)\n
    Field(ERAM, ByteAcc, NoLock, Preserve) { BYTE, 8 }\n
    Store(Arg1, BYTE)\n
}\n
Method (WECB, 3, Serialized)\n
// Arg0 - offset in bytes from zero-based EC\n
// Arg1 - size of buffer in bits\n
// Arg2 - value to write\n
{\n
    ShiftRight(Add(Arg1,7), 3, Arg1)\n
    Name(TEMP, Buffer(Arg1) { })\n
    Store(Arg2, TEMP)\n
    Add(Arg0, Arg1, Arg1)\n
    Store(0, Local0)\n
    While (LLess(Arg0, Arg1))\n
    {\n
        WE1B(Arg0, DerefOf(Index(TEMP, Local0)))\n
        Increment(Arg0)\n
        Increment(Local0)\n
    }\n
}\n
end;
Consider a hypothetical line of code that writes to BCT0:
Code:
   Store(Local0, BCT0)
In this case, the BCT0 access cannot be replaced with a call to RECB. It is a write, not a read. WECB must be used instead.

Code:
   WECB(0x30,128, Local0)
The first two parameters to WECB are the same as those to RECB (offset and size of the EC field). The third parameter (Arg2) is the value that should be written to the EC field. In this case it is the original source of the Store (the first parameter to Store)... Local0.

Store is not the only AML opcode that can write. Just as Store is not the only AML opcode that can read. Just as an example, consider Add:

Code:
   Add(X, Y, Z)
The example above reads from X, reads from Y, performs an addition... and writes the result to Z.

When you're not sure what an AML opcode does, read the ACPI spec. It is fully documented there, but outside the scope of this post.

The existing laptop repo is a good source of example and information. There are numerous WECB/RECB examples in the existing patches in the laptop repo.


Logic bug with charging/discharging status (AC adapter detection)

Some DSDTs suffer from a logic bug where at the point of 100% capacity (fully charged battery), an incorrect status is returned from _BST. This mostly affects certain ASUS laptops, but also a few others.

Here is a patch to correct this problem:
Code:
into method label FBST code_regex If\s\(CHGS\s\(Zero\)\)[\s]+\{[\s]+Store\s\(0x02,\sLocal0\)[\s]+\}[\s]+Else[\s]+\{[\s]+Store\s\(One,\sLocal0\)[\s]+\} replaceall_matched begin
If (CHGS (Zero))\n
{\n
     Store (0x02, Local0)\n
}\n
Else\n
{\n
     Store (Zero, Local0)\n
}
end;

Problem Reporting

Download patchmatic: https://bitbucket.org/RehabMan/os-x-maciasl-patchmatic/downloads/RehabMan-patchmatic-2015-0107.zip
Extract the 'patchmatic' binary from the ZIP. Copy it to /usr/bin, such that you have the binary at /usr/bin/patchmatic.

In terminal,
Code:
if [ -d ~/Downloads/RehabMan ]; then rm -R ~/Downloads/RehabMan; fi
mkdir ~/Downloads/RehabMan
cd ~/Downloads/RehabMan
patchmatic -extract
Note: It is easier if you use copy/paste instead of typing the commands manually.

Post contents of Downloads/RehabMan directory (as ZIP).

Also, post ioreg as ZIP: http://www.tonymacx86.com/audio/58368-guide-how-make-copy-ioreg.html. Please, use the IORegistryExplorer v2.1 attached to the post! DO NOT reply with an ioreg from any other version of IORegistryExplorer.app.

And output from:
Code:
kextstat|grep -y acpiplat
kextstat|grep -y appleintelcpu
kextstat|grep -y applelpc
Also, post EFI/Clover folder as ZIP (press F4 at main Clover screen before collecting). Please eliminate 'themes' directory, especially if you have an overabundance of themes installed. Provide only EFI/Clover, not the entire EFI folder.

Also post output of:
Code:
sudo touch /System/Library/Extensions && sudo kextcache -u /
Compress all files as ZIP. Do not use external links. Attach all files using site attachments only.


Contributing

If you do come up with a patch for your battery methods, you are encouraged to contribute to my patch repository, so please share your patch along with information about your computer, so I can add the patch to my laptop DSDT patch repository. Please provide both the text file containing the patches and the native DSDT (so I'm able to review the patches against the native DSDT). I will not add patches to the repository unless I can review the patches as they apply to the native DSDT.
 

Attachments

Last edited:
Joined
May 8, 2013
Messages
39
Motherboard
Intel NUC
CPU
i5-4250U
Graphics
HD5000
Mac
MacBook Air, Mac Pro
Classic Mac
Mobile Phone
How to patch DSDT for working battery status

:clap:WOW! Thank you! You are the STAR! Such a admirable work.

I tried it out with my own DSDT. It seemed I only have the 16 bit registers to patch.(hooray!)
But here comes my embarrasing....

The code in my DSDT to store value is:
Code:
Store (^^EC0.BVLT, local0)
Maybe that's the reason why my following patch doesn't work:
Code:
into method label _BST code_regex \BVLT, replaceall_matched begin B1B2(VLT0,VLT1), end;
And the funny thing is.. when I use regex to request replace "BVLT" , it only replaces "VLT", when I use the code above..

Is there any way to solve it please?

P.S. my patch code is:
Code:
# 16-bit registers
into device label EC0 code_regex BVLT,\s+16, replace_matched begin VLT0,8,VLT1,8, end;
into device label EC0 code_regex BRMC,\s+16, replace_matched begin RMC0,8,RMC1,8, end;
into device label EC0 code_regex BFCC,\s+16, replace_matched begin FCC0,8,FCC1,8, end;
into device label EC0 code_regex BDSC,\s+16, replace_matched begin DSC0,8,DSC1,8, end;
into device label EC0 code_regex BDSV,\s+16, replace_matched begin DSV0,8,DSV1,8, end;
into device label EC0 code_regex BPRA,\s+16, replace_matched begin PRA0,8,PRA1,8, end;


into method label B1B2 remove_entry;
into definitionblock code_regex . insert
begin
Method (B1B2, 2, NotSerialized) { Return(Or(Arg0, ShiftLeft(Arg1, 8))) }\n
end;


# fix 16-bit methods
into method label _BST code_regex \BVLT, replaceall_matched begin B1B2(VLT0,VLT1), end;
into method label _BST code_regex \BRMC, replaceall_matched begin B1B2(RMC0,RMC1), end;
into method label _BIF code_regex \BFCC, replaceall_matched begin B1B2(FCC0,FCC1), end;
into method label _BIF code_regex \BDSC, replaceall_matched begin B1B2(DSC0,DSC1), end;
into method label _BIF code_regex \BDSV, replaceall_matched begin B1B2(DSV0,DSV1), end;
into method label _BST code_regex \BPRA, replaceall_matched begin B1B2(PRA0,PRA1), end;
and here is my dsdt.amlView attachment ssdt+dsdt.zip
 

RehabMan

Moderator
Joined
May 3, 2012
Messages
191,731
Motherboard
Intel DH67BL
CPU
Core i7-2600K
Graphics
Intel HD 3000
Mac
MacBook Air
Mobile Phone
iOS
How to patch DSDT for working battery status

:clap:WOW! Thank you! You are the STAR! Such a admirable work.

I tried it out with my own DSDT. It seemed I only have the 16 bit registers to patch.(hooray!)
But here comes my embarrasing....

The code in my DSDT to store value is:
Code:
Store (^^EC0.BVLT, local0)
Maybe that's the reason why my following patch doesn't work:
Code:
into method label _BST code_regex \BVLT, replaceall_matched begin B1B2(VLT0,VLT1), end;
And the funny thing is.. when I use regex to request replace "BVLT" , it only replaces "VLT", when I use the code above..

Is there any way to solve it please?
You probably should read up on regular expressions. The backslash in a regex is an escape, so \B is probably not what you meant.

For that code above, you would want something like:
Code:
into method label _BST code_regex \(\^\^EC0\.BVLT, replaceall_matched begin (B1B2(^^EC0.VLT0,^^EC0.VLT1), end;
 
Joined
May 8, 2013
Messages
39
Motherboard
Intel NUC
CPU
i5-4250U
Graphics
HD5000
Mac
MacBook Air, Mac Pro
Classic Mac
Mobile Phone
How to patch DSDT for working battery status

You probably should read up on regular expressions. The backslash in a regex is an escape, so \B is probably not what you meant.

For that code above, you would want something like:
Code:
into method label _BST code_regex \(\^\^EC0\.BVLT, replaceall_matched begin (B1B2(^^EC0.VLT0,^^EC0.VLT1), end;

BIG thanks!:thumbup:
Got it worked out finally!Battery Status is now up!
Sorry for troubling you with those ignorant questions about the regex...
The format of "\(\^\^EC0\.BVLT" reminded me of LaTex! Haha.

Anyway, thanks a lot! here is the battery patch for Acer Aspire S7-391.View attachment Acer-S7-391 patch.txt
 

RehabMan

Moderator
Joined
May 3, 2012
Messages
191,731
Motherboard
Intel DH67BL
CPU
Core i7-2600K
Graphics
Intel HD 3000
Mac
MacBook Air
Mobile Phone
iOS
How to patch DSDT for working battery status

BIG thanks!:thumbup:
Got it worked out finally!Battery Status is now up!
Sorry for troubling you with those ignorant questions about the regex...
The format of "\(\^\^EC0\.BVLT" reminded me of LaTex! Haha.

Anyway, thanks a lot! here is the battery patch for Acer Aspire S7-391.View attachment 73967
Great!

I added your patch to the repo.
 
Joined
Dec 29, 2012
Messages
354
Motherboard
Dell XPS 18
CPU
Intel Core i3-3227U CPU @ 1.90GHz
Graphics
Intel HD Graphics 4000
Mac
Classic Mac
Mobile Phone
Dell XPS 18 - Is it possible?

In the example, mine differs from the one you wrote... Now as I've said before, I'm no programmer. However, I'm going to attempt this, and maybe search for some courses in this programming language if it's completely necessary.

The example shows:

OperationRegion (ECF2, EmbeddedControl, Zero, 0xFF)

Mine shows:

OperationRegion (ECMM, EmbeddedControl, Zero, 0X0100)

Should I just continue? Does this matter? I'm not sure if it will change things. This is the only location that "EmbeddedControl" shows up when I do a search through my dsdt.aml file.

I've also noticed that in your guide, all the 4-character lines end in #s (i.e. BCT0, BDN1, etc). Mine are mostly all letters, and the only ones with #s have the # in the second place (i.e. B0ST, B0CC).
 

RehabMan

Moderator
Joined
May 3, 2012
Messages
191,731
Motherboard
Intel DH67BL
CPU
Core i7-2600K
Graphics
Intel HD 3000
Mac
MacBook Air
Mobile Phone
iOS
How to patch DSDT for working battery status

In the example, mine differs from the one you wrote... Now as I've said before, I'm no programmer. However, I'm going to attempt this, and maybe search for some courses in this programming language if it's completely necessary.

The example shows:

OperationRegion (ECF2, EmbeddedControl, Zero, 0xFF)

Mine shows:

OperationRegion (ECMM, EmbeddedControl, Zero, 0X0100)

Should I just continue? Does this matter? I'm not sure if it will change things. This is the only location that "EmbeddedControl" shows up when I do a search through my dsdt.aml file.
Use the example DSDT to learn how it works and what you're looking to change. Then apply what you learn to your own DSDT.
 
Joined
Dec 29, 2012
Messages
354
Motherboard
Dell XPS 18
CPU
Intel Core i3-3227U CPU @ 1.90GHz
Graphics
Intel HD Graphics 4000
Mac
Classic Mac
Mobile Phone
Dell XPS 18 - Is it possible?

I've also noticed that in your guide, all the 4-character lines end in #s (i.e. BCT0, BDN1, etc). Mine are mostly all letters, and the only ones with #s have the # in the second place (i.e. B0ST, B0CC).

- - - Updated - - -

There is not much you need to know. Just follow the instructions...
And if I don't have Linux?
 

RehabMan

Moderator
Joined
May 3, 2012
Messages
191,731
Motherboard
Intel DH67BL
CPU
Core i7-2600K
Graphics
Intel HD 3000
Mac
MacBook Air
Mobile Phone
iOS
Dell XPS 18 - Is it possible?

I've also noticed that in your guide, all the 4-character lines end in #s (i.e. BCT0, BDN1, etc). Mine are mostly all letters, and the only ones with #s have the # in the second place (i.e. B0ST, B0CC).
The names don't matter.

And if I don't have Linux?
You download it. It is free.
 
Joined
Dec 29, 2012
Messages
354
Motherboard
Dell XPS 18
CPU
Intel Core i3-3227U CPU @ 1.90GHz
Graphics
Intel HD Graphics 4000
Mac
Classic Mac
Mobile Phone
Dell XPS 18 - Is it possible?

The names don't matter.
After reading through the first portion, and going through my own DSDT, I only have 8 lines (all 16 bit) that need to be patched. I guess, being a newbie to this entire thing, I got a lucky break.:think:

What I need to figure out is where I have to type in the changes... that's the part that I still don't get. On to more reading... :banghead:
 
Top