X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-kernel.git;a=blobdiff_plain;f=drivers%2Fchar%2Fi8k.c;h=6e40072fbf675e3c69b105cc8ea1d05491bba485;hp=d72433f2d310deb6cf909cb1feb30e92919887cd;hb=f3b19e7a066b0d5727df016a02d23edefbe75d94;hpb=f97b12cce6dea51880a6a89d4607c29c70a6a841 diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index d72433f2d310..50ca7f6a864c 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c @@ -5,6 +5,9 @@ * * Copyright (C) 2001 Massimo Dal Zotto * + * Hwmon integration: + * Copyright (C) 2011 Jean Delvare + * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any @@ -24,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -58,6 +63,8 @@ static DEFINE_MUTEX(i8k_mutex); static char bios_version[4]; +static char bios_machineid[16]; +static struct device *i8k_hwmon_dev; MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); @@ -71,13 +78,13 @@ static int ignore_dmi; module_param(ignore_dmi, bool, 0); MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); -static int restricted; +static int restricted = true; module_param(restricted, bool, 0); -MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set"); +MODULE_PARM_DESC(restricted, "Restrict fan control and serial number to CAP_SYS_ADMIN (default: 1)"); static int power_status; module_param(power_status, bool, 0600); -MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); +MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k (default: 0)"); static int fan_mult = I8K_FAN_MULT; module_param(fan_mult, int, 0); @@ -139,8 +146,8 @@ static int i8k_smm(struct smm_regs *regs) "movl %%edi,20(%%rax)\n\t" "popq %%rdx\n\t" "movl %%edx,0(%%rax)\n\t" - "lahf\n\t" - "shrl $8,%%eax\n\t" + "pushfq\n\t" + "popq %%rax\n\t" "andl $1,%%eax\n" :"=a"(rc) : "a"(regs) @@ -326,8 +333,11 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) break; case I8K_MACHINE_ID: - memset(buff, 0, 16); - strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff)); + if (restricted && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + memset(buff, 0, sizeof(buff)); + strlcpy(buff, bios_machineid, sizeof(buff)); break; case I8K_FN_STATUS: @@ -444,7 +454,7 @@ static int i8k_proc_show(struct seq_file *seq, void *offset) return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", I8K_PROC_FMT, bios_version, - i8k_get_dmi_data(DMI_PRODUCT_SERIAL), + (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : bios_machineid, cpu_temp, left_fan, right_fan, left_speed, right_speed, ac_power, fn_key); @@ -455,6 +465,152 @@ static int i8k_open_fs(struct inode *inode, struct file *file) return single_open(file, i8k_proc_show, NULL); } + +/* + * Hwmon interface + */ + +static ssize_t i8k_hwmon_show_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int cpu_temp; + + cpu_temp = i8k_get_temp(0); + if (cpu_temp < 0) + return cpu_temp; + return sprintf(buf, "%d\n", cpu_temp * 1000); +} + +static ssize_t i8k_hwmon_show_fan(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + int fan_speed; + + fan_speed = i8k_get_fan_speed(index); + if (fan_speed < 0) + return fan_speed; + return sprintf(buf, "%d\n", fan_speed); +} + +static ssize_t i8k_hwmon_show_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + static const char *labels[4] = { + "i8k", + "CPU", + "Left Fan", + "Right Fan", + }; + int index = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%s\n", labels[index]); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, + I8K_FAN_LEFT); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL, + I8K_FAN_RIGHT); +static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1); +static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2); +static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3); + +static void i8k_hwmon_remove_files(struct device *dev) +{ + device_remove_file(dev, &dev_attr_temp1_input); + device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr); + device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr); + device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr); + device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr); + device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr); + device_remove_file(dev, &sensor_dev_attr_name.dev_attr); +} + +static int __init i8k_init_hwmon(void) +{ + int err; + + i8k_hwmon_dev = hwmon_device_register(NULL); + if (IS_ERR(i8k_hwmon_dev)) { + err = PTR_ERR(i8k_hwmon_dev); + i8k_hwmon_dev = NULL; + printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err); + return err; + } + + /* Required name attribute */ + err = device_create_file(i8k_hwmon_dev, + &sensor_dev_attr_name.dev_attr); + if (err) + goto exit_unregister; + + /* CPU temperature attributes, if temperature reading is OK */ + err = i8k_get_temp(0); + if (err < 0) { + dev_dbg(i8k_hwmon_dev, + "Not creating temperature attributes (%d)\n", err); + } else { + err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input); + if (err) + goto exit_remove_files; + err = device_create_file(i8k_hwmon_dev, + &sensor_dev_attr_temp1_label.dev_attr); + if (err) + goto exit_remove_files; + } + + /* Left fan attributes, if left fan is present */ + err = i8k_get_fan_status(I8K_FAN_LEFT); + if (err < 0) { + dev_dbg(i8k_hwmon_dev, + "Not creating %s fan attributes (%d)\n", "left", err); + } else { + err = device_create_file(i8k_hwmon_dev, + &sensor_dev_attr_fan1_input.dev_attr); + if (err) + goto exit_remove_files; + err = device_create_file(i8k_hwmon_dev, + &sensor_dev_attr_fan1_label.dev_attr); + if (err) + goto exit_remove_files; + } + + /* Right fan attributes, if right fan is present */ + err = i8k_get_fan_status(I8K_FAN_RIGHT); + if (err < 0) { + dev_dbg(i8k_hwmon_dev, + "Not creating %s fan attributes (%d)\n", "right", err); + } else { + err = device_create_file(i8k_hwmon_dev, + &sensor_dev_attr_fan2_input.dev_attr); + if (err) + goto exit_remove_files; + err = device_create_file(i8k_hwmon_dev, + &sensor_dev_attr_fan2_label.dev_attr); + if (err) + goto exit_remove_files; + } + + return 0; + + exit_remove_files: + i8k_hwmon_remove_files(i8k_hwmon_dev); + exit_unregister: + hwmon_device_unregister(i8k_hwmon_dev); + return err; +} + +static void __exit i8k_exit_hwmon(void) +{ + i8k_hwmon_remove_files(i8k_hwmon_dev); + hwmon_device_unregister(i8k_hwmon_dev); +} + static struct dmi_system_id __initdata i8k_dmi_table[] = { { .ident = "Dell Inspiron", @@ -512,6 +668,13 @@ static struct dmi_system_id __initdata i8k_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"), }, }, + { + .ident = "Dell XPS421", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"), + }, + }, { } }; @@ -538,6 +701,8 @@ static int __init i8k_probe(void) } strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version)); + strlcpy(bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), + sizeof(bios_machineid)); /* * Get SMM Dell signature @@ -580,6 +745,7 @@ static int __init i8k_probe(void) static int __init i8k_init(void) { struct proc_dir_entry *proc_i8k; + int err; /* Are we running on an supported laptop? */ if (i8k_probe()) @@ -590,15 +756,24 @@ static int __init i8k_init(void) if (!proc_i8k) return -ENOENT; + err = i8k_init_hwmon(); + if (err) + goto exit_remove_proc; + printk(KERN_INFO "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n", I8K_VERSION); return 0; + + exit_remove_proc: + remove_proc_entry("i8k", NULL); + return err; } static void __exit i8k_exit(void) { + i8k_exit_hwmon(); remove_proc_entry("i8k", NULL); }