Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow fan control via character device (e.g. "echo 1 > /dev/acer-max-fan-0") #95

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,13 @@ Static mode coloring (zone=4 => most right zone, color=purple) and save it as ex
Load the previously saved profile:
`./facer_rgb.py -load example`

### Fan control

The fan speed can be controlled via character device as well, but at this time the only settings available are maximum and auto.

To set fans to the maximum speed, use `echo 1 > /dev/acer-fan-max-0` . To return them to automatic, use `echo 0 > /dev/acer-fan-max-0` .

Note that this is independent of the turbo mode, so use caution when returning your fans to automatic if your machine supports turbo overclocking.

## Known problems
If installation failed, check this [issue](https://github.com/JafarAkhondali/acer-predator-turbo-and-rgb-keyboard-linux-module/issues/4#issuecomment-905486393)
Expand Down
97 changes: 93 additions & 4 deletions src/facer.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ struct event_return_value {
#define GAMING_KBBL_STATIC_CHR "acer-gkbbl-static"
#define GAMING_KBBL_STATIC_CONFIG_LEN 4

/*
* Fan turbo-mode user-space communication
* A character drive will be exposed in /dev/acer-fan-max as a char block for switching the fans from auto to max
*/

#define GAMING_FAN_MAX_CHR "acer-fan-max"

/* Hotkey Customized Setting and Acer Application Status.
* Set Device Default Value and Report Acer Application Status.
* When Acer Application starts, it will run this method to inform
Expand Down Expand Up @@ -347,6 +354,7 @@ static struct wmi_interface *gaming_interface;

#define GAMING_KBBL_MINOR 0
#define GAMING_KBBL_STATIC_MINOR 0
#define GAMING_FAN_MAX_MINOR 0


static int dev_major; // Global variable to store major number of driver
Expand Down Expand Up @@ -379,8 +387,10 @@ static void __init set_quirks(void)
interface->capability |= ACER_CAP_BRIGHTNESS;

if (quirks->turbo)
interface->capability |= ACER_CAP_TURBO_OC | ACER_CAP_TURBO_LED
| ACER_CAP_TURBO_FAN;
interface->capability |= ACER_CAP_TURBO_OC | ACER_CAP_TURBO_LED;

if (quirks->cpu_fans | quirks->gpu_fans)
interface->capability |= ACER_CAP_TURBO_FAN;
}

static int __init dmi_matched(const struct dmi_system_id *dmi)
Expand Down Expand Up @@ -450,7 +460,6 @@ static struct quirk_entry quirk_acer_predator_ph317_54 = {
.gpu_fans = 1,
};
static struct quirk_entry quirk_acer_predator_ph517_51 = {
.turbo = 1,
.cpu_fans = 1,
.gpu_fans = 1,
};
Expand All @@ -460,7 +469,6 @@ static struct quirk_entry quirk_acer_predator_ph517_52 = {
.gpu_fans = 1,
};
static struct quirk_entry quirk_acer_predator_ph517_61 = {
.turbo = 1,
.cpu_fans = 1,
.gpu_fans = 1,
};
Expand Down Expand Up @@ -2150,6 +2158,85 @@ static int __init gaming_kbbl_static_cdev_init(void)
return 0;
}

static ssize_t gkbbl_max_fan_write(struct file *file, const char __user *buf, size_t count, loff_t *offset)
{
u8 turbo_buf[2]={0,0}; //allow null-terminated user space input (e.g. "echo 1 > /dev/acer-fan-max")
unsigned long err;
if (count > 2) {
pr_err("invalid data given to fan max fan setting. Data size %lu, expected 1 or 2\n",count);
return -EINVAL;
}
err = copy_from_user(turbo_buf, buf, count);
switch(turbo_buf[0]) {
case 0x00:
case 0x30: // ASCII 0
WMID_gaming_set_fan_mode(0x1); //auto mode
break;
case 0x01:
case 0x31: // ASCII 1
WMID_gaming_set_fan_mode(0x2); //turbo mode
break;
default:
pr_err("invalid data given to max fan setting. Expected 0 or 1\n");
}
return count;
}

static const struct file_operations gkbbl_max_fan_dev_fops = {
.owner = THIS_MODULE,
.write = gkbbl_max_fan_write
};

static struct class *gkbbl_max_fan_dev_class;
static struct gkbbl_device_data max_fan_static_dev_data;

static void __exit gkbbl_max_fan_cdev_exit(void)
{
device_destroy(gkbbl_max_fan_dev_class, MKDEV(dev_major, GAMING_FAN_MAX_MINOR));
class_unregister(gkbbl_max_fan_dev_class);
class_destroy(gkbbl_max_fan_dev_class);
unregister_chrdev_region(MKDEV(dev_major, GAMING_FAN_MAX_MINOR), MINORMASK);
}

static int gkbbl_static_max_fan_uevent(
#if RTLNX_VER_MIN(6, 2, 0)
const
#endif
struct device *dev, struct kobj_uevent_env *env)
{
add_uevent_var(env, "DEVMODE=%#o", 0666);
return 0;
}

static int __init gkbbl_max_fan_cdev_init(void)
{
dev_t dev;
int err;

err = alloc_chrdev_region(&dev, 0, 1, GAMING_FAN_MAX_CHR);
if (err < 0) {
pr_err("Char drive registering for gaming keyboard turbo fan failed: %d\n", err);
return err;
}

dev_major = MAJOR(dev);

gkbbl_max_fan_dev_class = class_create(THIS_MODULE, GAMING_FAN_MAX_CHR);
gkbbl_max_fan_dev_class->dev_uevent = gkbbl_static_max_fan_uevent;

cdev_init(&max_fan_static_dev_data.cdev, &gkbbl_max_fan_dev_fops);
max_fan_static_dev_data.cdev.owner = THIS_MODULE;

cdev_add(&max_fan_static_dev_data.cdev, MKDEV(dev_major, GAMING_FAN_MAX_MINOR), 1);

device_create(gkbbl_max_fan_dev_class, NULL, MKDEV(dev_major, GAMING_FAN_MAX_MINOR), NULL, "%s-%d",
GAMING_FAN_MAX_CHR,
GAMING_FAN_MAX_MINOR);

return 0;
}


static int __init gaming_kbbl_poll_and_enable_zones(void)
{
u64 gaming_sysinfo;
Expand Down Expand Up @@ -3067,6 +3154,7 @@ static int __init acer_wmi_init(void)
gaming_kbbl_cdev_init();
gaming_kbbl_static_cdev_init();
gaming_kbbl_poll_and_enable_zones();
gkbbl_max_fan_cdev_init();
}
}

Expand Down Expand Up @@ -3150,6 +3238,7 @@ static void __exit acer_wmi_exit(void)
if (wmi_has_guid(WMID_GUID4)) {
gaming_kbbl_cdev_exit();
gaming_kbbl_static_cdev_exit();
gkbbl_max_fan_cdev_exit();
}

remove_debugfs();
Expand Down