diff options
| -rw-r--r-- | drivers/platform/x86/dell-laptop.c | 31 |
1 files changed, 29 insertions, 2 deletions
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 1a0bfd43f8f0..04b2ddbe63d2 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
| @@ -128,6 +128,8 @@ static struct calling_interface_buffer *buffer; | |||
| 128 | struct page *bufferpage; | 128 | struct page *bufferpage; |
| 129 | DEFINE_MUTEX(buffer_mutex); | 129 | DEFINE_MUTEX(buffer_mutex); |
| 130 | 130 | ||
| 131 | static int hwswitch_state; | ||
| 132 | |||
| 131 | static void get_buffer(void) | 133 | static void get_buffer(void) |
| 132 | { | 134 | { |
| 133 | mutex_lock(&buffer_mutex); | 135 | mutex_lock(&buffer_mutex); |
| @@ -217,6 +219,8 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, | |||
| 217 | /* Derived from information in DellWirelessCtl.cpp: | 219 | /* Derived from information in DellWirelessCtl.cpp: |
| 218 | Class 17, select 11 is radio control. It returns an array of 32-bit values. | 220 | Class 17, select 11 is radio control. It returns an array of 32-bit values. |
| 219 | 221 | ||
| 222 | Input byte 0 = 0: Wireless information | ||
| 223 | |||
| 220 | result[0]: return code | 224 | result[0]: return code |
| 221 | result[1]: | 225 | result[1]: |
| 222 | Bit 0: Hardware switch supported | 226 | Bit 0: Hardware switch supported |
| @@ -237,18 +241,35 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, | |||
| 237 | Bits 20-31: Reserved | 241 | Bits 20-31: Reserved |
| 238 | result[2]: NVRAM size in bytes | 242 | result[2]: NVRAM size in bytes |
| 239 | result[3]: NVRAM format version number | 243 | result[3]: NVRAM format version number |
| 244 | |||
| 245 | Input byte 0 = 2: Wireless switch configuration | ||
| 246 | result[0]: return code | ||
| 247 | result[1]: | ||
| 248 | Bit 0: Wifi controlled by switch | ||
| 249 | Bit 1: Bluetooth controlled by switch | ||
| 250 | Bit 2: WWAN controlled by switch | ||
| 251 | Bits 3-6: Reserved | ||
| 252 | Bit 7: Wireless switch config locked | ||
| 253 | Bit 8: Wifi locator enabled | ||
| 254 | Bits 9-14: Reserved | ||
| 255 | Bit 15: Wifi locator setting locked | ||
| 256 | Bits 16-31: Reserved | ||
| 240 | */ | 257 | */ |
| 241 | 258 | ||
| 242 | static int dell_rfkill_set(void *data, bool blocked) | 259 | static int dell_rfkill_set(void *data, bool blocked) |
| 243 | { | 260 | { |
| 244 | int disable = blocked ? 1 : 0; | 261 | int disable = blocked ? 1 : 0; |
| 245 | unsigned long radio = (unsigned long)data; | 262 | unsigned long radio = (unsigned long)data; |
| 263 | int hwswitch_bit = (unsigned long)data - 1; | ||
| 246 | int ret = 0; | 264 | int ret = 0; |
| 247 | 265 | ||
| 248 | get_buffer(); | 266 | get_buffer(); |
| 249 | dell_send_request(buffer, 17, 11); | 267 | dell_send_request(buffer, 17, 11); |
| 250 | 268 | ||
| 251 | if (!(buffer->output[1] & BIT(16))) { | 269 | /* If the hardware switch controls this radio, and the hardware |
| 270 | switch is disabled, don't allow changing the software state */ | ||
| 271 | if ((hwswitch_state & BIT(hwswitch_bit)) && | ||
| 272 | !(buffer->output[1] & BIT(16))) { | ||
| 252 | ret = -EINVAL; | 273 | ret = -EINVAL; |
| 253 | goto out; | 274 | goto out; |
| 254 | } | 275 | } |
| @@ -265,6 +286,7 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data) | |||
| 265 | { | 286 | { |
| 266 | int status; | 287 | int status; |
| 267 | int bit = (unsigned long)data + 16; | 288 | int bit = (unsigned long)data + 16; |
| 289 | int hwswitch_bit = (unsigned long)data - 1; | ||
| 268 | 290 | ||
| 269 | get_buffer(); | 291 | get_buffer(); |
| 270 | dell_send_request(buffer, 17, 11); | 292 | dell_send_request(buffer, 17, 11); |
| @@ -272,7 +294,9 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data) | |||
| 272 | release_buffer(); | 294 | release_buffer(); |
| 273 | 295 | ||
| 274 | rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); | 296 | rfkill_set_sw_state(rfkill, !!(status & BIT(bit))); |
| 275 | rfkill_set_hw_state(rfkill, !(status & BIT(16))); | 297 | |
| 298 | if (hwswitch_state & (BIT(hwswitch_bit))) | ||
| 299 | rfkill_set_hw_state(rfkill, !(status & BIT(16))); | ||
| 276 | } | 300 | } |
| 277 | 301 | ||
| 278 | static const struct rfkill_ops dell_rfkill_ops = { | 302 | static const struct rfkill_ops dell_rfkill_ops = { |
| @@ -306,6 +330,9 @@ static int __init dell_setup_rfkill(void) | |||
| 306 | get_buffer(); | 330 | get_buffer(); |
| 307 | dell_send_request(buffer, 17, 11); | 331 | dell_send_request(buffer, 17, 11); |
| 308 | status = buffer->output[1]; | 332 | status = buffer->output[1]; |
| 333 | buffer->input[0] = 0x2; | ||
| 334 | dell_send_request(buffer, 17, 11); | ||
| 335 | hwswitch_state = buffer->output[1]; | ||
| 309 | release_buffer(); | 336 | release_buffer(); |
| 310 | 337 | ||
| 311 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { | 338 | if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { |
