dell-laptop: Pay attention to which devices the hardware switch controls
[safe/jmp/linux-2.6] / drivers / platform / x86 / dell-laptop.c
index 1a0bfd4..04b2ddb 100644 (file)
@@ -128,6 +128,8 @@ static struct calling_interface_buffer *buffer;
 struct page *bufferpage;
 DEFINE_MUTEX(buffer_mutex);
 
+static int hwswitch_state;
+
 static void get_buffer(void)
 {
        mutex_lock(&buffer_mutex);
@@ -217,6 +219,8 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
 /* Derived from information in DellWirelessCtl.cpp:
    Class 17, select 11 is radio control. It returns an array of 32-bit values.
 
+   Input byte 0 = 0: Wireless information
+
    result[0]: return code
    result[1]:
      Bit 0:      Hardware switch supported
@@ -237,18 +241,35 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
      Bits 20-31: Reserved
    result[2]: NVRAM size in bytes
    result[3]: NVRAM format version number
+
+   Input byte 0 = 2: Wireless switch configuration
+   result[0]: return code
+   result[1]:
+     Bit 0:      Wifi controlled by switch
+     Bit 1:      Bluetooth controlled by switch
+     Bit 2:      WWAN controlled by switch
+     Bits 3-6:   Reserved
+     Bit 7:      Wireless switch config locked
+     Bit 8:      Wifi locator enabled
+     Bits 9-14:  Reserved
+     Bit 15:     Wifi locator setting locked
+     Bits 16-31: Reserved
 */
 
 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 ret = 0;
 
        get_buffer();
        dell_send_request(buffer, 17, 11);
 
-       if (!(buffer->output[1] & BIT(16))) {
+       /* If the hardware switch controls this radio, and the hardware
+          switch is disabled, don't allow changing the software state */
+       if ((hwswitch_state & BIT(hwswitch_bit)) &&
+           !(buffer->output[1] & BIT(16))) {
                ret = -EINVAL;
                goto out;
        }
@@ -265,6 +286,7 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data)
 {
        int status;
        int bit = (unsigned long)data + 16;
+       int hwswitch_bit = (unsigned long)data - 1;
 
        get_buffer();
        dell_send_request(buffer, 17, 11);
@@ -272,7 +294,9 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data)
        release_buffer();
 
        rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
-       rfkill_set_hw_state(rfkill, !(status & BIT(16)));
+
+       if (hwswitch_state & (BIT(hwswitch_bit)))
+               rfkill_set_hw_state(rfkill, !(status & BIT(16)));
 }
 
 static const struct rfkill_ops dell_rfkill_ops = {
@@ -306,6 +330,9 @@ static int __init dell_setup_rfkill(void)
        get_buffer();
        dell_send_request(buffer, 17, 11);
        status = buffer->output[1];
+       buffer->input[0] = 0x2;
+       dell_send_request(buffer, 17, 11);
+       hwswitch_state = buffer->output[1];
        release_buffer();
 
        if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {