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.
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.
*** 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)
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, ...
Code:
Store (BDN0, BDN)
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,
Code:
Store (BCT0, CTN)
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,
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,
Code:
into device label H_EC code_regex BDC0,\s+16, replace_matched begin DC00,8,DC01,8, end;
Code:
Store (BDC0, Index (DerefOf (Index (Local0, 0x02)), Zero)) Store (ShiftRight (BDC0, 0x08), Index (DerefOf (Index (Local0, 0x02)), One))
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;
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))
Code:
into method label GBTI code_regex \(BDC0, replaceall_matched begin (B1B2(DC00,DC01), end;
Code:
Store (DC00, Index (DerefOf (Index (Local0, 0x02)), Zero)) Store (DC01, Index (DerefOf (Index (Local0, 0x02)), One))
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;
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;
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;
Code:
Store (BTY0, BTY) ... Store (BTY1, BTYB)
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;
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,
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;
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
Code:
... Store (BCT0, CTN) ... Store (BDN0, BDN) ... Store (BCT1, CTNB) ... Store (BDN1, BDNB) ...
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(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;
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"))
To handle the first case of BCT0, we want to do the equivalent of this:
Code:
Store(RECB(0x30,128), CTN)
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;
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(Arg1, 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;
Code:
Store(Local0, BCT0)
Code:
WECB(0x30,128, 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)
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.
Nenhum comentário:
Postar um comentário