summaryrefslogtreecommitdiffstats
path: root/drivers/misc/thinkpad_acpi.c
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-10-28 16:26:12 +0100
committerIngo Molnar <mingo@elte.hu>2008-10-28 16:26:12 +0100
commit7a9787e1eba95a166265e6a260cf30af04ef0a99 (patch)
treee730a4565e0318140d2fbd2f0415d18a339d7336 /drivers/misc/thinkpad_acpi.c
parent41b9eb264c8407655db57b60b4457fe1b2ec9977 (diff)
parent0173a3265b228da319ceb9c1ec6a5682fd1b2d92 (diff)
Merge commit 'v2.6.28-rc2' into x86/pci-ioapic-boot-irq-quirks
Diffstat (limited to 'drivers/misc/thinkpad_acpi.c')
-rw-r--r--drivers/misc/thinkpad_acpi.c541
1 files changed, 395 insertions, 146 deletions
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index b5969298f3d..4db1cf9078d 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -21,7 +21,7 @@
* 02110-1301, USA.
*/
-#define TPACPI_VERSION "0.20"
+#define TPACPI_VERSION "0.21"
#define TPACPI_SYSFS_VERSION 0x020200
/*
@@ -68,6 +68,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/input.h>
#include <linux/leds.h>
+#include <linux/rfkill.h>
#include <asm/uaccess.h>
#include <linux/dmi.h>
@@ -144,6 +145,12 @@ enum {
#define TPACPI_MAX_ACPI_ARGS 3
+/* rfkill switches */
+enum {
+ TPACPI_RFK_BLUETOOTH_SW_ID = 0,
+ TPACPI_RFK_WWAN_SW_ID,
+};
+
/* Debugging */
#define TPACPI_LOG TPACPI_FILE ": "
#define TPACPI_ERR KERN_ERR TPACPI_LOG
@@ -152,7 +159,6 @@ enum {
#define TPACPI_DEBUG KERN_DEBUG TPACPI_LOG
#define TPACPI_DBG_ALL 0xffff
-#define TPACPI_DBG_ALL 0xffff
#define TPACPI_DBG_INIT 0x0001
#define TPACPI_DBG_EXIT 0x0002
#define dbg_printk(a_dbg_level, format, arg...) \
@@ -536,7 +542,7 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
return -ENODEV;
}
- acpi_driver_data(ibm->acpi->device) = ibm;
+ ibm->acpi->device->driver_data = ibm;
sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
TPACPI_ACPI_EVENT_PREFIX,
ibm->name);
@@ -575,7 +581,8 @@ static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
if (!ibm->acpi->driver) {
- printk(TPACPI_ERR "kzalloc(ibm->driver) failed\n");
+ printk(TPACPI_ERR
+ "failed to allocate memory for ibm->acpi->driver\n");
return -ENOMEM;
}
@@ -831,6 +838,13 @@ static int parse_strtoul(const char *buf,
return 0;
}
+static void tpacpi_disable_brightness_delay(void)
+{
+ if (acpi_evalf(hkey_handle, NULL, "PWMS", "qvd", 0))
+ printk(TPACPI_NOTICE
+ "ACPI backlight control delay disabled\n");
+}
+
static int __init tpacpi_query_bcl_levels(acpi_handle handle)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -905,6 +919,43 @@ static int __init tpacpi_check_std_acpi_brightness_support(void)
return 0;
}
+static int __init tpacpi_new_rfkill(const unsigned int id,
+ struct rfkill **rfk,
+ const enum rfkill_type rfktype,
+ const char *name,
+ int (*toggle_radio)(void *, enum rfkill_state),
+ int (*get_state)(void *, enum rfkill_state *))
+{
+ int res;
+ enum rfkill_state initial_state;
+
+ *rfk = rfkill_allocate(&tpacpi_pdev->dev, rfktype);
+ if (!*rfk) {
+ printk(TPACPI_ERR
+ "failed to allocate memory for rfkill class\n");
+ return -ENOMEM;
+ }
+
+ (*rfk)->name = name;
+ (*rfk)->get_state = get_state;
+ (*rfk)->toggle_radio = toggle_radio;
+
+ if (!get_state(NULL, &initial_state))
+ (*rfk)->state = initial_state;
+
+ res = rfkill_register(*rfk);
+ if (res < 0) {
+ printk(TPACPI_ERR
+ "failed to register %s rfkill switch: %d\n",
+ name, res);
+ rfkill_free(*rfk);
+ *rfk = NULL;
+ return res;
+ }
+
+ return 0;
+}
+
/*************************************************************************
* thinkpad-acpi driver attributes
*/
@@ -1285,21 +1336,6 @@ static int hotkey_status_set(int status)
return 0;
}
-static void tpacpi_input_send_radiosw(void)
-{
- int wlsw;
-
- if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
- mutex_lock(&tpacpi_inputdev_send_mutex);
-
- input_report_switch(tpacpi_inputdev,
- SW_RFKILL_ALL, !!wlsw);
- input_sync(tpacpi_inputdev);
-
- mutex_unlock(&tpacpi_inputdev_send_mutex);
- }
-}
-
static void tpacpi_input_send_tabletsw(void)
{
int state;
@@ -1921,6 +1957,30 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
&dev_attr_hotkey_wakeup_hotunplug_complete.attr,
};
+static void bluetooth_update_rfk(void);
+static void wan_update_rfk(void);
+static void tpacpi_send_radiosw_update(void)
+{
+ int wlsw;
+
+ /* Sync these BEFORE sending any rfkill events */
+ if (tp_features.bluetooth)
+ bluetooth_update_rfk();
+ if (tp_features.wan)
+ wan_update_rfk();
+
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
+ mutex_lock(&tpacpi_inputdev_send_mutex);
+
+ input_report_switch(tpacpi_inputdev,
+ SW_RFKILL_ALL, !!wlsw);
+ input_sync(tpacpi_inputdev);
+
+ mutex_unlock(&tpacpi_inputdev_send_mutex);
+ }
+ hotkey_radio_sw_notify_change();
+}
+
static void hotkey_exit(void)
{
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
@@ -2086,6 +2146,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
if (!tp_features.hotkey)
return 1;
+ tpacpi_disable_brightness_delay();
+
hotkey_dev_attributes = create_attr_set(13, NULL);
if (!hotkey_dev_attributes)
return -ENOMEM;
@@ -2167,9 +2229,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
printk(TPACPI_INFO
"radio switch found; radios are %s\n",
enabled(status, 0));
+ }
+ if (tp_features.hotkey_wlsw)
res = add_to_attr_set(hotkey_dev_attributes,
&dev_attr_hotkey_radio_sw.attr);
- }
/* For X41t, X60t, X61t Tablets... */
if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
@@ -2287,7 +2350,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
tpacpi_inputdev->close = &hotkey_inputdev_close;
hotkey_poll_setup_safe(1);
- tpacpi_input_send_radiosw();
+ tpacpi_send_radiosw_update();
tpacpi_input_send_tabletsw();
return 0;
@@ -2419,8 +2482,7 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
case 7:
/* 0x7000-0x7FFF: misc */
if (tp_features.hotkey_wlsw && hkey == 0x7000) {
- tpacpi_input_send_radiosw();
- hotkey_radio_sw_notify_change();
+ tpacpi_send_radiosw_update();
send_acpi_ev = 0;
break;
}
@@ -2459,12 +2521,13 @@ static void hotkey_suspend(pm_message_t state)
static void hotkey_resume(void)
{
+ tpacpi_disable_brightness_delay();
+
if (hotkey_mask_get())
printk(TPACPI_ERR
"error while trying to read hot key mask "
"from firmware\n");
- tpacpi_input_send_radiosw();
- hotkey_radio_sw_notify_change();
+ tpacpi_send_radiosw_update();
hotkey_tablet_mode_notify_change();
hotkey_wakeup_reason_notify_change();
hotkey_wakeup_hotunplug_complete_notify_change();
@@ -2581,8 +2644,66 @@ enum {
TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */
};
-static int bluetooth_get_radiosw(void);
-static int bluetooth_set_radiosw(int radio_on);
+static struct rfkill *tpacpi_bluetooth_rfkill;
+
+static int bluetooth_get_radiosw(void)
+{
+ int status;
+
+ if (!tp_features.bluetooth)
+ return -ENODEV;
+
+ /* WLSW overrides bluetooth in firmware/hardware, reflect that */
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
+ return RFKILL_STATE_HARD_BLOCKED;
+
+ if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+ return -EIO;
+
+ return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0) ?
+ RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static void bluetooth_update_rfk(void)
+{
+ int status;
+
+ if (!tpacpi_bluetooth_rfkill)
+ return;
+
+ status = bluetooth_get_radiosw();
+ if (status < 0)
+ return;
+ rfkill_force_state(tpacpi_bluetooth_rfkill, status);
+}
+
+static int bluetooth_set_radiosw(int radio_on, int update_rfk)
+{
+ int status;
+
+ if (!tp_features.bluetooth)
+ return -ENODEV;
+
+ /* WLSW overrides bluetooth in firmware/hardware, but there is no
+ * reason to risk weird behaviour. */
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
+ && radio_on)
+ return -EPERM;
+
+ if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
+ return -EIO;
+ if (radio_on)
+ status |= TP_ACPI_BLUETOOTH_RADIOSSW;
+ else
+ status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
+ if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
+ return -EIO;
+
+ if (update_rfk)
+ bluetooth_update_rfk();
+
+ return 0;
+}
/* sysfs bluetooth enable ---------------------------------------------- */
static ssize_t bluetooth_enable_show(struct device *dev,
@@ -2595,7 +2716,8 @@ static ssize_t bluetooth_enable_show(struct device *dev,
if (status < 0)
return status;
- return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
}
static ssize_t bluetooth_enable_store(struct device *dev,
@@ -2608,7 +2730,7 @@ static ssize_t bluetooth_enable_store(struct device *dev,
if (parse_strtoul(buf, 1, &t))
return -EINVAL;
- res = bluetooth_set_radiosw(t);
+ res = bluetooth_set_radiosw(t, 1);
return (res) ? res : count;
}
@@ -2628,6 +2750,31 @@ static const struct attribute_group bluetooth_attr_group = {
.attrs = bluetooth_attributes,
};
+static int tpacpi_bluetooth_rfk_get(void *data, enum rfkill_state *state)
+{
+ int bts = bluetooth_get_radiosw();
+
+ if (bts < 0)
+ return bts;
+
+ *state = bts;
+ return 0;
+}
+
+static int tpacpi_bluetooth_rfk_set(void *data, enum rfkill_state state)
+{
+ return bluetooth_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
+}
+
+static void bluetooth_exit(void)
+{
+ if (tpacpi_bluetooth_rfkill)
+ rfkill_unregister(tpacpi_bluetooth_rfkill);
+
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ &bluetooth_attr_group);
+}
+
static int __init bluetooth_init(struct ibm_init_struct *iibm)
{
int res;
@@ -2646,57 +2793,32 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
str_supported(tp_features.bluetooth),
status);
- if (tp_features.bluetooth) {
- if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
- /* no bluetooth hardware present in system */
- tp_features.bluetooth = 0;
- dbg_printk(TPACPI_DBG_INIT,
- "bluetooth hardware not installed\n");
- } else {
- res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
- &bluetooth_attr_group);
- if (res)
- return res;
- }
+ if (tp_features.bluetooth &&
+ !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
+ /* no bluetooth hardware present in system */
+ tp_features.bluetooth = 0;
+ dbg_printk(TPACPI_DBG_INIT,
+ "bluetooth hardware not installed\n");
}
- return (tp_features.bluetooth)? 0 : 1;
-}
-
-static void bluetooth_exit(void)
-{
- sysfs_remove_group(&tpacpi_pdev->dev.kobj,
- &bluetooth_attr_group);
-}
-
-static int bluetooth_get_radiosw(void)
-{
- int status;
-
if (!tp_features.bluetooth)
- return -ENODEV;
-
- if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
- return -EIO;
-
- return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
-}
-
-static int bluetooth_set_radiosw(int radio_on)
-{
- int status;
+ return 1;
- if (!tp_features.bluetooth)
- return -ENODEV;
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &bluetooth_attr_group);
+ if (res)
+ return res;
- if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
- return -EIO;
- if (radio_on)
- status |= TP_ACPI_BLUETOOTH_RADIOSSW;
- else
- status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
- if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
- return -EIO;
+ res = tpacpi_new_rfkill(TPACPI_RFK_BLUETOOTH_SW_ID,
+ &tpacpi_bluetooth_rfkill,
+ RFKILL_TYPE_BLUETOOTH,
+ "tpacpi_bluetooth_sw",
+ tpacpi_bluetooth_rfk_set,
+ tpacpi_bluetooth_rfk_get);
+ if (res) {
+ bluetooth_exit();
+ return res;
+ }
return 0;
}
@@ -2711,7 +2833,8 @@ static int bluetooth_read(char *p)
len += sprintf(p + len, "status:\t\tnot supported\n");
else {
len += sprintf(p + len, "status:\t\t%s\n",
- (status)? "enabled" : "disabled");
+ (status == RFKILL_STATE_UNBLOCKED) ?
+ "enabled" : "disabled");
len += sprintf(p + len, "commands:\tenable, disable\n");
}
@@ -2727,9 +2850,9 @@ static int bluetooth_write(char *buf)
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
- bluetooth_set_radiosw(1);
+ bluetooth_set_radiosw(1, 1);
} else if (strlencmp(cmd, "disable") == 0) {
- bluetooth_set_radiosw(0);
+ bluetooth_set_radiosw(0, 1);
} else
return -EINVAL;
}
@@ -2755,8 +2878,66 @@ enum {
TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */
};
-static int wan_get_radiosw(void);
-static int wan_set_radiosw(int radio_on);
+static struct rfkill *tpacpi_wan_rfkill;
+
+static int wan_get_radiosw(void)
+{
+ int status;
+
+ if (!tp_features.wan)
+ return -ENODEV;
+
+ /* WLSW overrides WWAN in firmware/hardware, reflect that */
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
+ return RFKILL_STATE_HARD_BLOCKED;
+
+ if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+ return -EIO;
+
+ return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0) ?
+ RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
+}
+
+static void wan_update_rfk(void)
+{
+ int status;
+
+ if (!tpacpi_wan_rfkill)
+ return;
+
+ status = wan_get_radiosw();
+ if (status < 0)
+ return;
+ rfkill_force_state(tpacpi_wan_rfkill, status);
+}
+
+static int wan_set_radiosw(int radio_on, int update_rfk)
+{
+ int status;
+
+ if (!tp_features.wan)
+ return -ENODEV;
+
+ /* WLSW overrides bluetooth in firmware/hardware, but there is no
+ * reason to risk weird behaviour. */
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status
+ && radio_on)
+ return -EPERM;
+
+ if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
+ return -EIO;
+ if (radio_on)
+ status |= TP_ACPI_WANCARD_RADIOSSW;
+ else
+ status &= ~TP_ACPI_WANCARD_RADIOSSW;
+ if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
+ return -EIO;
+
+ if (update_rfk)
+ wan_update_rfk();
+
+ return 0;
+}
/* sysfs wan enable ---------------------------------------------------- */
static ssize_t wan_enable_show(struct device *dev,
@@ -2769,7 +2950,8 @@ static ssize_t wan_enable_show(struct device *dev,
if (status < 0)
return status;
- return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ (status == RFKILL_STATE_UNBLOCKED) ? 1 : 0);
}
static ssize_t wan_enable_store(struct device *dev,
@@ -2782,7 +2964,7 @@ static ssize_t wan_enable_store(struct device *dev,
if (parse_strtoul(buf, 1, &t))
return -EINVAL;
- res = wan_set_radiosw(t);
+ res = wan_set_radiosw(t, 1);
return (res) ? res : count;
}
@@ -2802,6 +2984,31 @@ static const struct attribute_group wan_attr_group = {
.attrs = wan_attributes,
};
+static int tpacpi_wan_rfk_get(void *data, enum rfkill_state *state)
+{
+ int wans = wan_get_radiosw();
+
+ if (wans < 0)
+ return wans;
+
+ *state = wans;
+ return 0;
+}
+
+static int tpacpi_wan_rfk_set(void *data, enum rfkill_state state)
+{
+ return wan_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
+}
+
+static void wan_exit(void)
+{
+ if (tpacpi_wan_rfkill)
+ rfkill_unregister(tpacpi_wan_rfkill);
+
+ sysfs_remove_group(&tpacpi_pdev->dev.kobj,
+ &wan_attr_group);
+}
+
static int __init wan_init(struct ibm_init_struct *iibm)
{
int res;
@@ -2818,57 +3025,32 @@ static int __init wan_init(struct ibm_init_struct *iibm)
str_supported(tp_features.wan),
status);
- if (tp_features.wan) {
- if (!(status & TP_ACPI_WANCARD_HWPRESENT)) {
- /* no wan hardware present in system */
- tp_features.wan = 0;
- dbg_printk(TPACPI_DBG_INIT,
- "wan hardware not installed\n");
- } else {
- res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
- &wan_attr_group);
- if (res)
- return res;
- }
+ if (tp_features.wan &&
+ !(status & TP_ACPI_WANCARD_HWPRESENT)) {
+ /* no wan hardware present in system */
+ tp_features.wan = 0;
+ dbg_printk(TPACPI_DBG_INIT,
+ "wan hardware not installed\n");
}
- return (tp_features.wan)? 0 : 1;
-}
-
-static void wan_exit(void)
-{
- sysfs_remove_group(&tpacpi_pdev->dev.kobj,
- &wan_attr_group);
-}
-
-static int wan_get_radiosw(void)
-{
- int status;
-
if (!tp_features.wan)
- return -ENODEV;
-
- if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
- return -EIO;
-
- return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0);
-}
-
-static int wan_set_radiosw(int radio_on)
-{
- int status;
+ return 1;
- if (!tp_features.wan)
- return -ENODEV;
+ res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
+ &wan_attr_group);
+ if (res)
+ return res;
- if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
- return -EIO;
- if (radio_on)
- status |= TP_ACPI_WANCARD_RADIOSSW;
- else
- status &= ~TP_ACPI_WANCARD_RADIOSSW;
- if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
- return -EIO;
+ res = tpacpi_new_rfkill(TPACPI_RFK_WWAN_SW_ID,
+ &tpacpi_wan_rfkill,
+ RFKILL_TYPE_WWAN,
+ "tpacpi_wwan_sw",
+ tpacpi_wan_rfk_set,
+ tpacpi_wan_rfk_get);
+ if (res) {
+ wan_exit();
+ return res;
+ }
return 0;
}
@@ -2883,7 +3065,8 @@ static int wan_read(char *p)
len += sprintf(p + len, "status:\t\tnot supported\n");
else {
len += sprintf(p + len, "status:\t\t%s\n",
- (status)? "enabled" : "disabled");
+ (status == RFKILL_STATE_UNBLOCKED) ?
+ "enabled" : "disabled");
len += sprintf(p + len, "commands:\tenable, disable\n");
}
@@ -2899,9 +3082,9 @@ static int wan_write(char *buf)
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
- wan_set_radiosw(1);
+ wan_set_radiosw(1, 1);
} else if (strlencmp(cmd, "disable") == 0) {
- wan_set_radiosw(0);
+ wan_set_radiosw(0, 1);
} else
return -EINVAL;
}
@@ -2914,7 +3097,6 @@ static struct ibm_struct wan_driver_data = {
.read = wan_read,
.write = wan_write,
.exit = wan_exit,
- .flags.experimental = 1,
};
/*************************************************************************
@@ -5812,6 +5994,52 @@ static void fan_exit(void)
flush_workqueue(tpacpi_wq);
}
+static void fan_suspend(pm_message_t state)
+{
+ if (!fan_control_allowed)
+ return;
+
+ /* Store fan status in cache */
+ fan_get_status_safe(NULL);
+ if (tp_features.fan_ctrl_status_undef)
+ fan_control_desired_level = TP_EC_FAN_AUTO;
+}
+
+static void fan_resume(void)
+{
+ u8 saved_fan_level;
+ u8 current_level = 7;
+ bool do_set = false;
+
+ /* DSDT *always* updates status on resume */
+ tp_features.fan_ctrl_status_undef = 0;
+
+ saved_fan_level = fan_control_desired_level;
+ if (!fan_control_allowed ||
+ (fan_get_status_safe(&current_level) < 0))
+ return;
+
+ switch (fan_control_access_mode) {
+ case TPACPI_FAN_WR_ACPI_SFAN:
+ do_set = (saved_fan_level > current_level);
+ break;
+ case TPACPI_FAN_WR_ACPI_FANS:
+ case TPACPI_FAN_WR_TPEC:
+ do_set = ((saved_fan_level & TP_EC_FAN_FULLSPEED) ||
+ (saved_fan_level == 7 &&
+ !(current_level & TP_EC_FAN_FULLSPEED)));
+ break;
+ default:
+ return;
+ }
+ if (do_set) {
+ printk(TPACPI_NOTICE
+ "restoring fan level to 0x%02x\n",
+ saved_fan_level);
+ fan_set_level_safe(saved_fan_level);
+ }
+}
+
static int fan_read(char *p)
{
int len = 0;
@@ -6003,6 +6231,8 @@ static struct ibm_struct fan_driver_data = {
.read = fan_read,
.write = fan_write,
.exit = fan_exit,
+ .suspend = fan_suspend,
+ .resume = fan_resume,
};
/****************************************************************************
@@ -6168,13 +6398,18 @@ err_out:
/* Probing */
-static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
+/* returns 0 - probe ok, or < 0 - probe error.
+ * Probe ok doesn't mean thinkpad found.
+ * On error, kfree() cleanup on tp->* is not performed, caller must do it */
+static int __must_check __init get_thinkpad_model_data(
+ struct thinkpad_id_data *tp)
{
const struct dmi_device *dev = NULL;
char ec_fw_string[18];
+ char const *s;
if (!tp)
- return;
+ return -EINVAL;
memset(tp, 0, sizeof(*tp));
@@ -6183,12 +6418,14 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
else if (dmi_name_in_vendors("LENOVO"))
tp->vendor = PCI_VENDOR_ID_LENOVO;
else
- return;
+ return 0;
- tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION),
- GFP_KERNEL);
+ s = dmi_get_system_info(DMI_BIOS_VERSION);
+ tp->bios_version_str = kstrdup(s, GFP_KERNEL);
+ if (s && !tp->bios_version_str)
+ return -ENOMEM;
if (!tp->bios_version_str)
- return;
+ return 0;
tp->bios_model = tp->bios_version_str[0]
| (tp->bios_version_str[1] << 8);
@@ -6207,21 +6444,27 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
+ if (!tp->ec_version_str)
+ return -ENOMEM;
tp->ec_model = ec_fw_string[0]
| (ec_fw_string[1] << 8);
break;
}
}
- tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION),
- GFP_KERNEL);
- if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) {
- kfree(tp->model_str);
- tp->model_str = NULL;
+ s = dmi_get_system_info(DMI_PRODUCT_VERSION);
+ if (s && !strnicmp(s, "ThinkPad", 8)) {
+ tp->model_str = kstrdup(s, GFP_KERNEL);
+ if (!tp->model_str)
+ return -ENOMEM;
}
- tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME),
- GFP_KERNEL);
+ s = dmi_get_system_info(DMI_PRODUCT_NAME);
+ tp->nummodel_str = kstrdup(s, GFP_KERNEL);
+ if (s && !tp->nummodel_str)
+ return -ENOMEM;
+
+ return 0;
}
static int __init probe_for_thinkpad(void)
@@ -6484,7 +6727,13 @@ static int __init thinkpad_acpi_module_init(void)
/* Driver-level probe */
- get_thinkpad_model_data(&thinkpad_id);
+ ret = get_thinkpad_model_data(&thinkpad_id);
+ if (ret) {
+ printk(TPACPI_ERR
+ "unable to get DMI data: %d\n", ret);
+ thinkpad_acpi_module_exit();
+ return ret;
+ }
ret = probe_for_thinkpad();
if (ret) {
thinkpad_acpi_module_exit();