|
@@ -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
|
|
@@ -905,6 +912,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 +1329,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 +1950,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
|
|
@@ -2167,9 +2220,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 +2341,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 +2473,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;
|
|
|
}
|
|
@@ -2463,8 +2516,7 @@ static void hotkey_resume(void)
|
|
|
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 +2633,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 +2705,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 +2719,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 +2739,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 +2782,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 +2822,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 +2839,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 +2867,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 +2939,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 +2953,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 +2973,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 +3014,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 +3054,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 +3071,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;
|
|
|
}
|
|
@@ -6168,13 +6340,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 +6360,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 +6386,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 +6669,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();
|