Sunday, June 12, 2016

How to enable performance fan mode on Lenovo Z580

If you have Lenovo Z580 as me, you can see that you can set only silent, normal and dust cleaning mode for your fan. It's because of incompatibility of current ideapad-laptop module. Enabling performance mode is very easy.

All you need is my modified ideapad-laptop.c. All the magic is around line 408

397:  static ssize_t store_ideapad_fan(struct device *dev,   
398:             struct device_attribute *attr,   
399:             const char *buf, size_t count)   
400:   {   
401:     int ret, state;   
402:     struct ideapad_private *priv = dev_get_drvdata(dev);   
403:     if (!count)   
404:        return 0;   
405:     if (sscanf(buf, "%i", &state) != 1)   
406:        return -EINVAL;     
407:     if (state < 0 || state > 4 || state == 3)   
408:        return -EINVAL;   
409:     ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);   
410:     if (ret < 0)   
411:        return -EIO;   
412:     return count;   
413:   }   

In my modified code here:

397:  static ssize_t store_ideapad_fan(struct device *dev,   
398:             struct device_attribute *attr,   
399:             const char *buf, size_t count)   
400:   {   
401:     int ret, state;   
402:     struct ideapad_private *priv = dev_get_drvdata(dev);   
403:     if (!count)   
404:        return 0;   
405:     if (sscanf(buf, "%i", &state) != 1)   
406:        return -EINVAL;   
407:     if (state != 8)   
408:        if (state < 0 || state > 4 || state == 3)   
409:          return -EINVAL;   
410:     ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);   
411:     if (ret < 0)   
412:        return -EIO;   
413:     return count;   
414:   }   

is just added option to accept number 8, because our laptops uses different VCI number. You can make our modified ideapad-laptop.c with this Makefile

1:  ifneq ($(KERNELRELEASE),)  
2:  obj-m := ideapad-laptop.o  
3:  else  
4:  KDIR ?= /lib/modules/`uname -r`/build  
5:  all: ideapad-laptop  
6:  ideapad-laptop: ideapad-laptop.c  
7:       $(MAKE) -C $(KDIR) M=$$PWD  
8:  clean:  
9:       rm -f *.o *.ko modules.order Module.symvers *.mod.c  
10:  endif  

Then you need to compress it and replace original driver with this one.

 # rm /lib/modules/`uname -r`/kernel/drivers/platform/x86/ideapad-laptop.ko.gz  
 # cp ideapad-laptop.ko.gz /lib/modules/`uname -r`/kernel/drivers/platform/x86/ideapad-laptop.ko.gz  

After this, reboot and it should work with

 # echo 8 > /sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/VPC2004:00/fan_mode  

Or you can use this simple C program to switch different modes
1:  #include <stdio.h>  
2:  #include <stdlib.h>  
3:  #include <unistd.h>  
4:  #include <string.h>  
5:  int main(int argc, char *argv[])  
6:  {  
7:    if (argc == 2)  
8:    {  
9:     setuid(0);  
10:     if (strcmp(argv[1],"normal") == 0)  
11:       system("echo 2 > /sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/VPC2004:00/fan_mode");  
12:     else if (strcmp(argv[1], "silent") == 0)  
13:       system("echo 4 > /sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/VPC2004:00/fan_mode");  
14:     else if (strcmp(argv[1], "performance") == 0)  
15:       system("echo 8 > /sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/VPC2004:00/fan_mode");  
16:     else if (strcmp(argv[1], "dust") == 0)  
17:       system("echo 1 > /sys/devices/pci0000:00/0000:00:1f.0/PNP0C09:00/VPC2004:00/fan_mode");  
18:    }  
19:    return 0;  
20:  }  

Compile it and run:
$ gcc -std=C11 fan-mode.c -o fan-mode  
$ sudo mv fan-mode /bin  
$ sudo chown root:root /bin/fan-mode  
$ sudo chmod +s /bin/fan-mode  
$ sudo chmod +x /bin/fan-mode  
$ fan-mode performance  

It has SUID bit of root, because only root has permission to change fan mode and it's placed in bin, so you can use it as regular command.
 

1 comment:

  1. Don't use system(), especially if you're running setuid. You also don't need to setuid(0) if you're making it +s and owned by root - it will run as root already.

    ReplyDelete