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.
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