|
@@ -169,6 +169,7 @@ enum {
|
|
|
enum {
|
|
|
TPACPI_RFK_BLUETOOTH_SW_ID = 0,
|
|
|
TPACPI_RFK_WWAN_SW_ID,
|
|
|
+ TPACPI_RFK_UWB_SW_ID,
|
|
|
};
|
|
|
|
|
|
/* Debugging */
|
|
@@ -261,6 +262,7 @@ static struct {
|
|
|
u32 bright_16levels:1;
|
|
|
u32 bright_acpimode:1;
|
|
|
u32 wan:1;
|
|
|
+ u32 uwb:1;
|
|
|
u32 fan_ctrl_status_undef:1;
|
|
|
u32 input_device_registered:1;
|
|
|
u32 platform_drv_registered:1;
|
|
@@ -317,6 +319,8 @@ static int dbg_bluetoothemul;
|
|
|
static int tpacpi_bluetooth_emulstate;
|
|
|
static int dbg_wwanemul;
|
|
|
static int tpacpi_wwan_emulstate;
|
|
|
+static int dbg_uwbemul;
|
|
|
+static int tpacpi_uwb_emulstate;
|
|
|
#endif
|
|
|
|
|
|
|
|
@@ -967,6 +971,7 @@ static int __init tpacpi_new_rfkill(const unsigned int id,
|
|
|
struct rfkill **rfk,
|
|
|
const enum rfkill_type rfktype,
|
|
|
const char *name,
|
|
|
+ const bool set_default,
|
|
|
int (*toggle_radio)(void *, enum rfkill_state),
|
|
|
int (*get_state)(void *, enum rfkill_state *))
|
|
|
{
|
|
@@ -978,7 +983,7 @@ static int __init tpacpi_new_rfkill(const unsigned int id,
|
|
|
printk(TPACPI_ERR
|
|
|
"failed to read initial state for %s, error %d; "
|
|
|
"will turn radio off\n", name, res);
|
|
|
- } else {
|
|
|
+ } else if (set_default) {
|
|
|
/* try to set the initial state as the default for the rfkill
|
|
|
* type, since we ask the firmware to preserve it across S5 in
|
|
|
* NVRAM */
|
|
@@ -1148,6 +1153,31 @@ static DRIVER_ATTR(wwan_emulstate, S_IWUSR | S_IRUGO,
|
|
|
tpacpi_driver_wwan_emulstate_show,
|
|
|
tpacpi_driver_wwan_emulstate_store);
|
|
|
|
|
|
+/* uwb_emulstate ------------------------------------------------- */
|
|
|
+static ssize_t tpacpi_driver_uwb_emulstate_show(
|
|
|
+ struct device_driver *drv,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ return snprintf(buf, PAGE_SIZE, "%d\n", !!tpacpi_uwb_emulstate);
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t tpacpi_driver_uwb_emulstate_store(
|
|
|
+ struct device_driver *drv,
|
|
|
+ const char *buf, size_t count)
|
|
|
+{
|
|
|
+ unsigned long t;
|
|
|
+
|
|
|
+ if (parse_strtoul(buf, 1, &t))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ tpacpi_uwb_emulstate = !!t;
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static DRIVER_ATTR(uwb_emulstate, S_IWUSR | S_IRUGO,
|
|
|
+ tpacpi_driver_uwb_emulstate_show,
|
|
|
+ tpacpi_driver_uwb_emulstate_store);
|
|
|
#endif
|
|
|
|
|
|
/* --------------------------------------------------------------------- */
|
|
@@ -1175,6 +1205,8 @@ static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
|
|
|
res = driver_create_file(drv, &driver_attr_bluetooth_emulstate);
|
|
|
if (!res && dbg_wwanemul)
|
|
|
res = driver_create_file(drv, &driver_attr_wwan_emulstate);
|
|
|
+ if (!res && dbg_uwbemul)
|
|
|
+ res = driver_create_file(drv, &driver_attr_uwb_emulstate);
|
|
|
#endif
|
|
|
|
|
|
return res;
|
|
@@ -1191,6 +1223,7 @@ static void tpacpi_remove_driver_attributes(struct device_driver *drv)
|
|
|
driver_remove_file(drv, &driver_attr_wlsw_emulstate);
|
|
|
driver_remove_file(drv, &driver_attr_bluetooth_emulstate);
|
|
|
driver_remove_file(drv, &driver_attr_wwan_emulstate);
|
|
|
+ driver_remove_file(drv, &driver_attr_uwb_emulstate);
|
|
|
#endif
|
|
|
}
|
|
|
|
|
@@ -2125,6 +2158,7 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
|
|
|
|
|
|
static void bluetooth_update_rfk(void);
|
|
|
static void wan_update_rfk(void);
|
|
|
+static void uwb_update_rfk(void);
|
|
|
static void tpacpi_send_radiosw_update(void)
|
|
|
{
|
|
|
int wlsw;
|
|
@@ -2134,6 +2168,8 @@ static void tpacpi_send_radiosw_update(void)
|
|
|
bluetooth_update_rfk();
|
|
|
if (tp_features.wan)
|
|
|
wan_update_rfk();
|
|
|
+ if (tp_features.uwb)
|
|
|
+ uwb_update_rfk();
|
|
|
|
|
|
if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {
|
|
|
mutex_lock(&tpacpi_inputdev_send_mutex);
|
|
@@ -3035,6 +3071,7 @@ static int __init bluetooth_init(struct ibm_init_struct *iibm)
|
|
|
&tpacpi_bluetooth_rfkill,
|
|
|
RFKILL_TYPE_BLUETOOTH,
|
|
|
"tpacpi_bluetooth_sw",
|
|
|
+ true,
|
|
|
tpacpi_bluetooth_rfk_set,
|
|
|
tpacpi_bluetooth_rfk_get);
|
|
|
if (res) {
|
|
@@ -3309,6 +3346,7 @@ static int __init wan_init(struct ibm_init_struct *iibm)
|
|
|
&tpacpi_wan_rfkill,
|
|
|
RFKILL_TYPE_WWAN,
|
|
|
"tpacpi_wwan_sw",
|
|
|
+ true,
|
|
|
tpacpi_wan_rfk_set,
|
|
|
tpacpi_wan_rfk_get);
|
|
|
if (res) {
|
|
@@ -3365,6 +3403,162 @@ static struct ibm_struct wan_driver_data = {
|
|
|
.shutdown = wan_shutdown,
|
|
|
};
|
|
|
|
|
|
+/*************************************************************************
|
|
|
+ * UWB subdriver
|
|
|
+ */
|
|
|
+
|
|
|
+enum {
|
|
|
+ /* ACPI GUWB/SUWB bits */
|
|
|
+ TP_ACPI_UWB_HWPRESENT = 0x01, /* UWB hw available */
|
|
|
+ TP_ACPI_UWB_RADIOSSW = 0x02, /* UWB radio enabled */
|
|
|
+};
|
|
|
+
|
|
|
+static struct rfkill *tpacpi_uwb_rfkill;
|
|
|
+
|
|
|
+static int uwb_get_radiosw(void)
|
|
|
+{
|
|
|
+ int status;
|
|
|
+
|
|
|
+ if (!tp_features.uwb)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ /* WLSW overrides UWB in firmware/hardware, reflect that */
|
|
|
+ if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&status) && !status)
|
|
|
+ return RFKILL_STATE_HARD_BLOCKED;
|
|
|
+
|
|
|
+#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
|
|
|
+ if (dbg_uwbemul)
|
|
|
+ return (tpacpi_uwb_emulstate) ?
|
|
|
+ RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (!acpi_evalf(hkey_handle, &status, "GUWB", "d"))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return ((status & TP_ACPI_UWB_RADIOSSW) != 0) ?
|
|
|
+ RFKILL_STATE_UNBLOCKED : RFKILL_STATE_SOFT_BLOCKED;
|
|
|
+}
|
|
|
+
|
|
|
+static void uwb_update_rfk(void)
|
|
|
+{
|
|
|
+ int status;
|
|
|
+
|
|
|
+ if (!tpacpi_uwb_rfkill)
|
|
|
+ return;
|
|
|
+
|
|
|
+ status = uwb_get_radiosw();
|
|
|
+ if (status < 0)
|
|
|
+ return;
|
|
|
+ rfkill_force_state(tpacpi_uwb_rfkill, status);
|
|
|
+}
|
|
|
+
|
|
|
+static int uwb_set_radiosw(int radio_on, int update_rfk)
|
|
|
+{
|
|
|
+ int status;
|
|
|
+
|
|
|
+ if (!tp_features.uwb)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ /* WLSW overrides UWB 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;
|
|
|
+
|
|
|
+#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
|
|
|
+ if (dbg_uwbemul) {
|
|
|
+ tpacpi_uwb_emulstate = !!radio_on;
|
|
|
+ if (update_rfk)
|
|
|
+ uwb_update_rfk();
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ status = (radio_on) ? TP_ACPI_UWB_RADIOSSW : 0;
|
|
|
+ if (!acpi_evalf(hkey_handle, NULL, "SUWB", "vd", status))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ if (update_rfk)
|
|
|
+ uwb_update_rfk();
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* --------------------------------------------------------------------- */
|
|
|
+
|
|
|
+static int tpacpi_uwb_rfk_get(void *data, enum rfkill_state *state)
|
|
|
+{
|
|
|
+ int uwbs = uwb_get_radiosw();
|
|
|
+
|
|
|
+ if (uwbs < 0)
|
|
|
+ return uwbs;
|
|
|
+
|
|
|
+ *state = uwbs;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tpacpi_uwb_rfk_set(void *data, enum rfkill_state state)
|
|
|
+{
|
|
|
+ return uwb_set_radiosw((state == RFKILL_STATE_UNBLOCKED), 0);
|
|
|
+}
|
|
|
+
|
|
|
+static void uwb_exit(void)
|
|
|
+{
|
|
|
+ if (tpacpi_uwb_rfkill)
|
|
|
+ rfkill_unregister(tpacpi_uwb_rfkill);
|
|
|
+}
|
|
|
+
|
|
|
+static int __init uwb_init(struct ibm_init_struct *iibm)
|
|
|
+{
|
|
|
+ int res;
|
|
|
+ int status = 0;
|
|
|
+
|
|
|
+ vdbg_printk(TPACPI_DBG_INIT, "initializing uwb subdriver\n");
|
|
|
+
|
|
|
+ TPACPI_ACPIHANDLE_INIT(hkey);
|
|
|
+
|
|
|
+ tp_features.uwb = hkey_handle &&
|
|
|
+ acpi_evalf(hkey_handle, &status, "GUWB", "qd");
|
|
|
+
|
|
|
+ vdbg_printk(TPACPI_DBG_INIT, "uwb is %s, status 0x%02x\n",
|
|
|
+ str_supported(tp_features.uwb),
|
|
|
+ status);
|
|
|
+
|
|
|
+#ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES
|
|
|
+ if (dbg_uwbemul) {
|
|
|
+ tp_features.uwb = 1;
|
|
|
+ printk(TPACPI_INFO
|
|
|
+ "uwb switch emulation enabled\n");
|
|
|
+ } else
|
|
|
+#endif
|
|
|
+ if (tp_features.uwb &&
|
|
|
+ !(status & TP_ACPI_UWB_HWPRESENT)) {
|
|
|
+ /* no uwb hardware present in system */
|
|
|
+ tp_features.uwb = 0;
|
|
|
+ dbg_printk(TPACPI_DBG_INIT,
|
|
|
+ "uwb hardware not installed\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!tp_features.uwb)
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ res = tpacpi_new_rfkill(TPACPI_RFK_UWB_SW_ID,
|
|
|
+ &tpacpi_uwb_rfkill,
|
|
|
+ RFKILL_TYPE_UWB,
|
|
|
+ "tpacpi_uwb_sw",
|
|
|
+ false,
|
|
|
+ tpacpi_uwb_rfk_set,
|
|
|
+ tpacpi_uwb_rfk_get);
|
|
|
+
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+static struct ibm_struct uwb_driver_data = {
|
|
|
+ .name = "uwb",
|
|
|
+ .exit = uwb_exit,
|
|
|
+ .flags.experimental = 1,
|
|
|
+};
|
|
|
+
|
|
|
/*************************************************************************
|
|
|
* Video subdriver
|
|
|
*/
|
|
@@ -6830,6 +7024,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
|
|
|
.init = wan_init,
|
|
|
.data = &wan_driver_data,
|
|
|
},
|
|
|
+ {
|
|
|
+ .init = uwb_init,
|
|
|
+ .data = &uwb_driver_data,
|
|
|
+ },
|
|
|
#ifdef CONFIG_THINKPAD_ACPI_VIDEO
|
|
|
{
|
|
|
.init = video_init,
|
|
@@ -6986,6 +7184,12 @@ MODULE_PARM_DESC(dbg_wwanemul, "Enables WWAN switch emulation");
|
|
|
module_param_named(wwan_state, tpacpi_wwan_emulstate, bool, 0);
|
|
|
MODULE_PARM_DESC(wwan_state,
|
|
|
"Initial state of the emulated WWAN switch");
|
|
|
+
|
|
|
+module_param(dbg_uwbemul, uint, 0);
|
|
|
+MODULE_PARM_DESC(dbg_uwbemul, "Enables UWB switch emulation");
|
|
|
+module_param_named(uwb_state, tpacpi_uwb_emulstate, bool, 0);
|
|
|
+MODULE_PARM_DESC(uwb_state,
|
|
|
+ "Initial state of the emulated UWB switch");
|
|
|
#endif
|
|
|
|
|
|
static void thinkpad_acpi_module_exit(void)
|