-
-
Save daringer/721e2cea17b570512097 to your computer and use it in GitHub Desktop.
| /** | |
| * ASUS Zenbook Fan control module, verified with: | |
| * - UX32VD Zenbook | |
| * - .... | |
| * | |
| * Just 'make' and copy the fan.ko file to /lib/modules/`uname -r`/... | |
| * If the modules loads succesfully it will bring up a "thermal_cooling_device" | |
| * like /sys/devices/virtual/thermal/cooling_deviceX/ mostly providing | |
| * cur_state / max_state | |
| * | |
| * PLEASE USE WITH CAUTION, you can easily overheat your machine with a wrong | |
| * manually set fan speed... | |
| * | |
| **/ | |
| #include <linux/module.h> | |
| #include <linux/kernel.h> | |
| #include <linux/init.h> | |
| #include <linux/acpi.h> | |
| #include <linux/thermal.h> | |
| #include <linux/dmi.h> | |
| MODULE_AUTHOR("Felipe Contreras <[email protected]>"); | |
| MODULE_AUTHOR("Markus Meissner <[email protected]>"); | |
| MODULE_DESCRIPTION("ASUS fan driver"); | |
| MODULE_LICENSE("GPL"); | |
| static struct thermal_cooling_device *cdev; | |
| static int last_state; | |
| static bool is_manual; | |
| static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state); | |
| static int fan_set_auto(struct thermal_cooling_device *cdev); | |
| static int fan_set(struct thermal_cooling_device *cdev, int fan, int speed); | |
| static int fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state); | |
| static void __exit fan_exit(void); | |
| static int __init fan_init(void); | |
| static int fan_get_max_state(struct thermal_cooling_device *cdev, | |
| unsigned long *state) | |
| { | |
| *state = 0xff; | |
| return 0; | |
| } | |
| static int fan_get_cur_state(struct thermal_cooling_device *cdev, | |
| unsigned long *state) | |
| { | |
| struct acpi_object_list params; | |
| union acpi_object in_objs[1]; | |
| unsigned long long value; | |
| acpi_status r; | |
| params.count = ARRAY_SIZE(in_objs); | |
| params.pointer = in_objs; | |
| in_objs[0].type = ACPI_TYPE_INTEGER; | |
| in_objs[0].integer.value = 0; | |
| // fan does not report during manual speed setting - so fake it! | |
| if (is_manual) { | |
| *state = last_state; | |
| return 0; | |
| } | |
| r = acpi_evaluate_integer(NULL, "\\_TZ.RFAN", ¶ms, &value); | |
| if (r != AE_OK) | |
| return r; | |
| *state = value; | |
| return 0; | |
| } | |
| static int fan_set(struct thermal_cooling_device *cdev, int fan, int speed) | |
| { | |
| struct acpi_object_list params; | |
| union acpi_object in_objs[2]; | |
| unsigned long long value; | |
| params.count = ARRAY_SIZE(in_objs); | |
| params.pointer = in_objs; | |
| in_objs[0].type = ACPI_TYPE_INTEGER; | |
| in_objs[0].integer.value = fan; | |
| in_objs[1].type = ACPI_TYPE_INTEGER; | |
| in_objs[1].integer.value = speed; | |
| return acpi_evaluate_integer(NULL, "\\_SB.PCI0.LPCB.EC0.SFNV", ¶ms, &value); | |
| } | |
| static int fan_set_cur_state(struct thermal_cooling_device *cdev, | |
| unsigned long state) | |
| { | |
| last_state = state; | |
| // setting fan to automatic, if cur_state is set to (0x0100) 256 | |
| if(state == 256) { | |
| is_manual = false; | |
| return fan_set_auto(cdev); | |
| } else { | |
| is_manual = true; | |
| return fan_set(cdev, 1, state); | |
| } | |
| } | |
| static int fan_set_auto(struct thermal_cooling_device *cdev) | |
| { | |
| return fan_set(cdev, 0, 0); | |
| } | |
| static const struct thermal_cooling_device_ops fan_cooling_ops = { | |
| .get_max_state = fan_get_max_state, | |
| .get_cur_state = fan_get_cur_state, | |
| .set_cur_state = fan_set_cur_state, | |
| }; | |
| static int __init fan_init(void) | |
| { | |
| if (strcmp(dmi_get_system_info(DMI_SYS_VENDOR), "ASUSTeK COMPUTER INC.")) | |
| return -ENODEV; | |
| cdev = thermal_cooling_device_register("Fan", NULL, &fan_cooling_ops); | |
| last_state = -1; | |
| is_manual = false; | |
| if (IS_ERR(cdev)) | |
| return PTR_ERR(cdev); | |
| fan_set_auto(cdev); | |
| return 0; | |
| } | |
| static void __exit fan_exit(void) | |
| { | |
| fan_set_auto(cdev); | |
| thermal_cooling_device_unregister(cdev); | |
| } | |
| module_init(fan_init); | |
| module_exit(fan_exit); |
| #!/bin/bash | |
| startpath="/sys/devices/virtual/thermal/" | |
| path=$(grep -r Fan ${startpath}/cooling_device*/type 2> /dev/null | \ | |
| cut -d ":" -f 1 | xargs dirname) | |
| if [[ "$1" = "" ]]; then | |
| echo "Usage: $0 <action>" | |
| exit; | |
| fi | |
| if [[ "$1" = "max" || "$1" = "full" || "$1" = "get_max" ]]; then | |
| cat ${path}/max_state | |
| elif [[ "$1" = "cur" || "$1" = "get" || "$1" = "current" ]]; then | |
| cat ${path}/cur_state | |
| elif [[ "$1" = "set" && "$2" != "" ]]; then | |
| echo $2 > ${path}/cur_state | |
| echo "Set to 256 to reactivate automatic regulation!" | |
| fi |
| obj-m := asus_fan.o | |
| KDIR := /lib/modules/$(shell uname -r)/build | |
| PWD := $(shell pwd) | |
| all: | |
| $(MAKE) -C $(KDIR) M=$(PWD) modules | |
| install: | |
| # just copy the .ko file anywhere below: | |
| # /lib/modules/$(uname -r)/ | |
| # | |
| # finally add it to some on-boot-load-mechanism | |
| # the module will _not_ automatically load. |
@Fenisu works fine for me on Fedora 20 and kernel 3.15.3
@Venemo thanks, yes missed that for some reason!? but updated and pushed it for the sake of completeness - and yes still works fine here on Arch with 3.15.6 ....
Although the location/number of the cooling_device changes from boot to boot, so I would suggest to either avoid rebooting or setup a oneshot-systemd service to link it to a known place on each reboot?
Anyone ever had the automatic regulator go above 40% fan speed? Lately my Zenbook feels hotter as usual, but the fan does not even think about going faster^^
Haha, it started bugging me already.... uploaded minimal bash-script to get around this...
Does it still work for you ? I have issues with it on Archlinux (updated 3.16.4.1). It doesn't do anything but it used to work... Asking since the state of my fans is pretty crazy right now: full speed anytime with no correlation to temperature !
Just now upgraded to 3.16.4-1-ck on an Arch System, still works - more or less. Mine does the opposite, never going above 40% even if the cpu reaches 85° or more ... Should maybe first go for a inner-cleanup - Although, did you rebuild the modules? does the script find the path? Can you set the fan-speed over console, by yourself using echo? Additionally, the user might not have the permission to write to /sys/....../cooling_device5/ .
- added a workaround to also get a fan speed, if it was previously set manually
- moved the code to a github repository - https://github.com/daringer/asus-fan
@daringer Thanks for the update. Your stuff works wonderfully on Fedora 21 with kernel 3.17.2! :)
wohooo, I've made something useful :D the recent version adds support for the second fan for the dedicated gfx if there is one ... As there are new Zenbooks announced, which seem to use a similar/identical interface, I'll try to (somehow) push it towards mainstream - but haven't done this before, lets see how far I get before I'll start to cry the first time ;DDD
edit: and stop writing here, fill the issues in https://github.com/daringer/asus-fan with stuff you need ...
how to use all this ?!
made 3 files in a directory and ran make install and it gave me error
makefile:6: *** missing separator. Stop.
i guess i will stop using gnu linux os since hardware manufacturers use gnu linux for themselves to boot a cd or in their hardware tools but dont want to support it.
It also complains that the makefile file name is written in lowercase instead of lowercase with a capital M. It should be "Makefile".
And I can't get it to work on Ubuntu 14.04, it loads, but nothing happens. No new "thermal_cooling_device" appears in /sys/devices/virtual/thermal/cooling_deviceX/, I had and still have 7 different, all having cur_state at 0, and won't change if I try.