It can be changed at runtime so make sure that dell-laptop knows always
current state. It can be modified by userspace utility smbios-wireless-ctl.

Signed-off-by: Pali Rohár <pali.ro...@gmail.com>
---
 drivers/platform/x86/dell-laptop.c |   85 ++++++++++++++++++++++++++----------
 1 file changed, 61 insertions(+), 24 deletions(-)

diff --git a/drivers/platform/x86/dell-laptop.c 
b/drivers/platform/x86/dell-laptop.c
index d9710c1..89d6004 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -308,8 +308,6 @@ static const struct dmi_system_id dell_quirks[] __initconst 
= {
 static struct calling_interface_buffer *buffer;
 static DEFINE_MUTEX(buffer_mutex);
 
-static int hwswitch_state;
-
 static void clear_buffer(void)
 {
        memset(buffer, 0, sizeof(struct calling_interface_buffer));
@@ -552,20 +550,30 @@ static int dell_rfkill_set(void *data, bool blocked)
        int disable = blocked ? 1 : 0;
        unsigned long radio = (unsigned long)data;
        int hwswitch_bit = (unsigned long)data - 1;
+       int hwswitch;
+       int status;
        int ret;
 
        get_buffer();
 
        dell_send_request(buffer, 17, 11);
        ret = buffer->output[0];
+       status = buffer->output[1];
 
        if (ret != 0)
                goto out;
 
+       clear_buffer();
+
+       buffer->input[0] = 0x2;
+       dell_send_request(buffer, 17, 11);
+       ret = buffer->output[0];
+       hwswitch = buffer->output[1];
+
        /* If the hardware switch controls this radio, and the hardware
           switch is disabled, always disable the radio */
-       if ((hwswitch_state & BIT(hwswitch_bit)) &&
-           !(buffer->output[1] & BIT(16)))
+       if (ret == 0 && (hwswitch & BIT(hwswitch_bit)) &&
+           (status & BIT(0)) && !(status & BIT(16)))
                disable = 1;
 
        clear_buffer();
@@ -596,27 +604,43 @@ static void dell_rfkill_update_sw_state(struct rfkill 
*rfkill, int radio,
 }
 
 static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
-                                       int status)
+                                       int status, int hwswitch)
 {
-       if (hwswitch_state & (BIT(radio - 1)))
+       if (hwswitch & (BIT(radio - 1)))
                rfkill_set_hw_state(rfkill, !(status & BIT(16)));
 }
 
 static void dell_rfkill_query(struct rfkill *rfkill, void *data)
 {
+       int radio = ((unsigned long)data & 0xF);
+       int hwswitch;
        int status;
        int ret;
 
        get_buffer();
+
        dell_send_request(buffer, 17, 11);
        ret = buffer->output[0];
        status = buffer->output[1];
+
+       if (ret != 0 || !(status & BIT(0))) {
+               release_buffer();
+               return;
+       }
+
+       clear_buffer();
+
+       buffer->input[0] = 0x2;
+       dell_send_request(buffer, 17, 11);
+       ret = buffer->output[0];
+       hwswitch = buffer->output[1];
+
        release_buffer();
 
        if (ret != 0)
                return;
 
-       dell_rfkill_update_hw_state(rfkill, (unsigned long)data, status);
+       dell_rfkill_update_hw_state(rfkill, radio, status, hwswitch);
 }
 
 static const struct rfkill_ops dell_rfkill_ops = {
@@ -628,13 +652,24 @@ static struct dentry *dell_laptop_dir;
 
 static int dell_debugfs_show(struct seq_file *s, void *data)
 {
+       int hwswitch_state;
+       int hwswitch_ret;
        int status;
        int ret;
 
        get_buffer();
+
        dell_send_request(buffer, 17, 11);
        ret = buffer->output[0];
        status = buffer->output[1];
+
+       clear_buffer();
+
+       buffer->input[0] = 0x2;
+       dell_send_request(buffer, 17, 11);
+       hwswitch_ret = buffer->output[0];
+       hwswitch_state = buffer->output[1];
+
        release_buffer();
 
        seq_printf(s, "return:\t%d\n", ret);
@@ -679,7 +714,8 @@ static int dell_debugfs_show(struct seq_file *s, void *data)
        seq_printf(s, "Bit 21: WiGig is blocked:            %lu\n",
                  (status & BIT(21)) >> 21);
 
-       seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
+       seq_printf(s, "\nhwswitch_return:\t%d\n", hwswitch_ret);
+       seq_printf(s, "hwswitch_state:\t0x%X\n", hwswitch_state);
        seq_printf(s, "Bit 0 : Wifi controlled by switch:      %lu\n",
                   hwswitch_state & BIT(0));
        seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
@@ -715,6 +751,7 @@ static const struct file_operations dell_debugfs_fops = {
 
 static void dell_update_rfkill(struct work_struct *ignored)
 {
+       int hwswitch = 0;
        int status;
        int ret;
 
@@ -727,16 +764,26 @@ static void dell_update_rfkill(struct work_struct 
*ignored)
        if (ret != 0)
                goto out;
 
+       clear_buffer();
+
+       buffer->input[0] = 0x2;
+       dell_send_request(buffer, 17, 11);
+       ret = buffer->output[0];
+
+       if (ret == 0 && (status & BIT(0)))
+               hwswitch = buffer->output[1];
+
        if (wifi_rfkill) {
-               dell_rfkill_update_hw_state(wifi_rfkill, 1, status);
+               dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
                dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
        }
        if (bluetooth_rfkill) {
-               dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status);
+               dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
+                                           hwswitch);
                dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
        }
        if (wwan_rfkill) {
-               dell_rfkill_update_hw_state(wwan_rfkill, 3, status);
+               dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
                dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
        }
 
@@ -804,25 +851,15 @@ static int __init dell_setup_rfkill(void)
        dell_send_request(buffer, 17, 11);
        ret = buffer->output[0];
        status = buffer->output[1];
-       clear_buffer();
-       buffer->input[0] = 0x2;
-       dell_send_request(buffer, 17, 11);
-       hwswitch_state = buffer->output[1];
        release_buffer();
 
        /* dell wireless info smbios call is not supported */
        if (ret != 0)
                return 0;
 
-       if (!(status & BIT(0))) {
-               if (force_rfkill) {
-                       /* No hwsitch, clear all hw-controlled bits */
-                       hwswitch_state &= ~7;
-               } else {
-                       /* rfkill is only tested on laptops with a hwswitch */
-                       return 0;
-               }
-       }
+       /* rfkill is only tested on laptops with a hwswitch */
+       if (!(status & BIT(0)) && !force_rfkill)
+               return 0;
 
        if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
                wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe platform-driver-x86" 
in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to