Example: ASUSTeK COMPUTER INC. X5511CA

Analyzing the DSDT DSL

By looking at the DSL file, we find the methods we are interested in:

        Method (SFNV, 2, Serialized)
        {
            If ((Arg0 == Zero))
            {
                If ((DECF & One))
                {
                    Local0 = RRAM (0x0521)
                    Local0 |= 0x80
                    WRAM (0x0521, Local0)
                }

                If ((DECF & 0x02))
                {
                    Local0 = RRAM (0x0522)
                    Local0 |= 0x80
                    WRAM (0x0522, Local0)
                }

                DECF = Zero
                Return (Zero)
            }

            If ((Arg0 == One))
            {
                Local0 = RRAM (0x0521)
                Local0 &= 0x7F
                WRAM (0x0521, Local0)
                DECF |= One
                ST84 (Zero, Arg1)
                Return (Zero)
            }

            If ((Arg0 == 0x02))
            {
                Local0 = RRAM (0x0522)
                Local0 &= 0x7F
                WRAM (0x0522, Local0)
                DECF |= 0x02
                ST84 (One, Arg1)
                Return (Zero)
            }

            Return (Zero)
        }

        [...]

        Method (RRAM, 1, Serialized)
        {
            If (ECAV ())
            {
                Acquire (MU4T, 0xFFFF)
                If ((WEOE () != Zero))
                {
                    Release (MU4T)
                    Return (Ones)
                }

                If ((WEIE () != Zero))
                {
                    Release (MU4T)
                    Return (Ones)
                }

                EC4C = 0xFF
                If ((WEIE () != Zero))
                {
                    Release (MU4T)
                    Return (Ones)
                }

                Local0 = Arg0
                Local1 = (Local0 & 0xFF)
                Local0 >>= 0x08
                Local0 &= 0xFF
                EC4C = 0x80
                If ((WEIE () != Zero))
                {
                    Release (MU4T)
                    Return (Ones)
                }

                EC4D = Local0
                If ((WEIE () != Zero))
                {
                    Release (MU4T)
                    Return (Ones)
                }

                EC4D = Local1
                If ((WEIE () != Zero))
                {
                    Release (MU4T)
                    Return (Ones)
                }

                If ((WEOF () != Zero))
                {
                    Release (MU4T)
                    Return (Ones)
                }

                Local0 = EC4D /* \_SB_.PCI0.LPCB.EC0_.EC4D */
                Release (MU4T)
                Return (Local0)
            }

            Return (Ones)
        }

Finding the Arguments for the Write Method

Let's have a look at the SFNV method from above:

Let's try calling the SFNV method:

sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 1 0
sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 1 255
sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 0 0

The first command stops the fan, the second sets the fan to full speed, and the third resets the fan.

Result:

Finding the Read Register

We couldn't find any register or method that reports the fan speed.

However, it must be set somewhere in the EC RAM.

Luckily, we have the RRAM method to read values out of the EC RAM.

Let's make a dump of all EC RAM registers when the fan is stopped:

sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 1 0
for i in $(seq 0 65535); do
    printf "$i: "; sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.RRAM' $i
done > dump_low

Then we turn the fan up and create another dump:

sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.SFNV' 1 255
for i in $(seq 0 65535); do
    printf "$i: "; sudo ec_probe acpi_call '\_SB.PCI0.LPCB.EC0.RRAM' $i
done > dump_high

Now we can compare the two dumps to identify changes, e.g. by using:

diff dump_low dump_high

We see that register 6151 contains 0 in dump_low and 255 in dump_high.

Result:

Writing the Configuration File

See the configuration file on GitHub

Note

Maybe there is another way to control the fan by using the QMOD method and the TAH0, TAH1, TH1R, TH1L, TH0R and TH0L registers.

However, I was not able to figure it out.