aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-11-17 08:00:16 -0500
committerMatthew Garrett <matthew.garrett@nebula.com>2013-11-20 18:50:48 -0500
commit4cc8a57425c623753b10b77b15392e5b83baa5a3 (patch)
treed220df5c61ad219637c843671df10125f4348174
parent527d1511310a89650000081869260394e20c7013 (diff)
Revert "dell-laptop: Remove rfkill code"
Without rfkill functionality in dell-laptop I have the following problems: -If the hardware radio switch is set to disable the radio, then userspace will still think it can use wireless and bluetooth. -The wwan / 3g modem cannot be soft blocked without the dell-laptop rfkill functionality I know the rfkill functionality was removed from the dell-laptop driver because it caused more problems then it fixed, and the blacklist for it was growing out of control. But in the thread discussing this Dell mentioned that they only QA the rfkill acpi interface on Latitudes and indeed there have been no blacklist entries for Latitudes. Therefor I would like to bring the rfkill functionality back only for Latitudes. This patch is a straight-forward revert. The next patch in this set will drop the blacklist and replace it with a Latitude check. This reverts commit a6c2390cd6d2083d27a2359658e08f2d3df375ac. Conflicts: drivers/platform/x86/dell-laptop.c Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Matthew Garrett <matthew.garrett@nebula.com>
-rw-r--r--drivers/platform/x86/dell-laptop.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index bb77e18b3dd4..55f75a2d5168 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -21,6 +21,7 @@
21#include <linux/err.h> 21#include <linux/err.h>
22#include <linux/dmi.h> 22#include <linux/dmi.h>
23#include <linux/io.h> 23#include <linux/io.h>
24#include <linux/rfkill.h>
24#include <linux/power_supply.h> 25#include <linux/power_supply.h>
25#include <linux/acpi.h> 26#include <linux/acpi.h>
26#include <linux/mm.h> 27#include <linux/mm.h>
@@ -89,6 +90,9 @@ static struct platform_driver platform_driver = {
89 90
90static struct platform_device *platform_device; 91static struct platform_device *platform_device;
91static struct backlight_device *dell_backlight_device; 92static struct backlight_device *dell_backlight_device;
93static struct rfkill *wifi_rfkill;
94static struct rfkill *bluetooth_rfkill;
95static struct rfkill *wwan_rfkill;
92 96
93static const struct dmi_system_id dell_device_table[] __initconst = { 97static const struct dmi_system_id dell_device_table[] __initconst = {
94 { 98 {
@@ -115,6 +119,53 @@ static const struct dmi_system_id dell_device_table[] __initconst = {
115}; 119};
116MODULE_DEVICE_TABLE(dmi, dell_device_table); 120MODULE_DEVICE_TABLE(dmi, dell_device_table);
117 121
122static struct dmi_system_id dell_blacklist[] = {
123 /* Supported by compal-laptop */
124 {
125 .ident = "Dell Mini 9",
126 .matches = {
127 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
128 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
129 },
130 },
131 {
132 .ident = "Dell Mini 10",
133 .matches = {
134 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
135 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
136 },
137 },
138 {
139 .ident = "Dell Mini 10v",
140 .matches = {
141 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
142 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
143 },
144 },
145 {
146 .ident = "Dell Mini 1012",
147 .matches = {
148 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
149 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
150 },
151 },
152 {
153 .ident = "Dell Inspiron 11z",
154 .matches = {
155 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
156 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
157 },
158 },
159 {
160 .ident = "Dell Mini 12",
161 .matches = {
162 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
163 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
164 },
165 },
166 {}
167};
168
118static struct dmi_system_id dell_quirks[] = { 169static struct dmi_system_id dell_quirks[] = {
119 { 170 {
120 .callback = dmi_matched, 171 .callback = dmi_matched,
@@ -355,6 +406,94 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
355 return buffer; 406 return buffer;
356} 407}
357 408
409/* Derived from information in DellWirelessCtl.cpp:
410 Class 17, select 11 is radio control. It returns an array of 32-bit values.
411
412 Input byte 0 = 0: Wireless information
413
414 result[0]: return code
415 result[1]:
416 Bit 0: Hardware switch supported
417 Bit 1: Wifi locator supported
418 Bit 2: Wifi is supported
419 Bit 3: Bluetooth is supported
420 Bit 4: WWAN is supported
421 Bit 5: Wireless keyboard supported
422 Bits 6-7: Reserved
423 Bit 8: Wifi is installed
424 Bit 9: Bluetooth is installed
425 Bit 10: WWAN is installed
426 Bits 11-15: Reserved
427 Bit 16: Hardware switch is on
428 Bit 17: Wifi is blocked
429 Bit 18: Bluetooth is blocked
430 Bit 19: WWAN is blocked
431 Bits 20-31: Reserved
432 result[2]: NVRAM size in bytes
433 result[3]: NVRAM format version number
434
435 Input byte 0 = 2: Wireless switch configuration
436 result[0]: return code
437 result[1]:
438 Bit 0: Wifi controlled by switch
439 Bit 1: Bluetooth controlled by switch
440 Bit 2: WWAN controlled by switch
441 Bits 3-6: Reserved
442 Bit 7: Wireless switch config locked
443 Bit 8: Wifi locator enabled
444 Bits 9-14: Reserved
445 Bit 15: Wifi locator setting locked
446 Bits 16-31: Reserved
447*/
448
449static int dell_rfkill_set(void *data, bool blocked)
450{
451 int disable = blocked ? 1 : 0;
452 unsigned long radio = (unsigned long)data;
453 int hwswitch_bit = (unsigned long)data - 1;
454 int ret = 0;
455
456 get_buffer();
457 dell_send_request(buffer, 17, 11);
458
459 /* If the hardware switch controls this radio, and the hardware
460 switch is disabled, don't allow changing the software state */
461 if ((hwswitch_state & BIT(hwswitch_bit)) &&
462 !(buffer->output[1] & BIT(16))) {
463 ret = -EINVAL;
464 goto out;
465 }
466
467 buffer->input[0] = (1 | (radio<<8) | (disable << 16));
468 dell_send_request(buffer, 17, 11);
469
470out:
471 release_buffer();
472 return ret;
473}
474
475static void dell_rfkill_query(struct rfkill *rfkill, void *data)
476{
477 int status;
478 int bit = (unsigned long)data + 16;
479 int hwswitch_bit = (unsigned long)data - 1;
480
481 get_buffer();
482 dell_send_request(buffer, 17, 11);
483 status = buffer->output[1];
484 release_buffer();
485
486 rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
487
488 if (hwswitch_state & (BIT(hwswitch_bit)))
489 rfkill_set_hw_state(rfkill, !(status & BIT(16)));
490}
491
492static const struct rfkill_ops dell_rfkill_ops = {
493 .set_block = dell_rfkill_set,
494 .query = dell_rfkill_query,
495};
496
358static struct dentry *dell_laptop_dir; 497static struct dentry *dell_laptop_dir;
359 498
360static int dell_debugfs_show(struct seq_file *s, void *data) 499static int dell_debugfs_show(struct seq_file *s, void *data)
@@ -424,6 +563,108 @@ static const struct file_operations dell_debugfs_fops = {
424 .release = single_release, 563 .release = single_release,
425}; 564};
426 565
566static void dell_update_rfkill(struct work_struct *ignored)
567{
568 if (wifi_rfkill)
569 dell_rfkill_query(wifi_rfkill, (void *)1);
570 if (bluetooth_rfkill)
571 dell_rfkill_query(bluetooth_rfkill, (void *)2);
572 if (wwan_rfkill)
573 dell_rfkill_query(wwan_rfkill, (void *)3);
574}
575static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
576
577
578static int __init dell_setup_rfkill(void)
579{
580 int status;
581 int ret;
582
583 if (dmi_check_system(dell_blacklist)) {
584 pr_info("Blacklisted hardware detected - not enabling rfkill\n");
585 return 0;
586 }
587
588 get_buffer();
589 dell_send_request(buffer, 17, 11);
590 status = buffer->output[1];
591 buffer->input[0] = 0x2;
592 dell_send_request(buffer, 17, 11);
593 hwswitch_state = buffer->output[1];
594 release_buffer();
595
596 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
597 wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
598 RFKILL_TYPE_WLAN,
599 &dell_rfkill_ops, (void *) 1);
600 if (!wifi_rfkill) {
601 ret = -ENOMEM;
602 goto err_wifi;
603 }
604 ret = rfkill_register(wifi_rfkill);
605 if (ret)
606 goto err_wifi;
607 }
608
609 if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
610 bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
611 &platform_device->dev,
612 RFKILL_TYPE_BLUETOOTH,
613 &dell_rfkill_ops, (void *) 2);
614 if (!bluetooth_rfkill) {
615 ret = -ENOMEM;
616 goto err_bluetooth;
617 }
618 ret = rfkill_register(bluetooth_rfkill);
619 if (ret)
620 goto err_bluetooth;
621 }
622
623 if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
624 wwan_rfkill = rfkill_alloc("dell-wwan",
625 &platform_device->dev,
626 RFKILL_TYPE_WWAN,
627 &dell_rfkill_ops, (void *) 3);
628 if (!wwan_rfkill) {
629 ret = -ENOMEM;
630 goto err_wwan;
631 }
632 ret = rfkill_register(wwan_rfkill);
633 if (ret)
634 goto err_wwan;
635 }
636
637 return 0;
638err_wwan:
639 rfkill_destroy(wwan_rfkill);
640 if (bluetooth_rfkill)
641 rfkill_unregister(bluetooth_rfkill);
642err_bluetooth:
643 rfkill_destroy(bluetooth_rfkill);
644 if (wifi_rfkill)
645 rfkill_unregister(wifi_rfkill);
646err_wifi:
647 rfkill_destroy(wifi_rfkill);
648
649 return ret;
650}
651
652static void dell_cleanup_rfkill(void)
653{
654 if (wifi_rfkill) {
655 rfkill_unregister(wifi_rfkill);
656 rfkill_destroy(wifi_rfkill);
657 }
658 if (bluetooth_rfkill) {
659 rfkill_unregister(bluetooth_rfkill);
660 rfkill_destroy(bluetooth_rfkill);
661 }
662 if (wwan_rfkill) {
663 rfkill_unregister(wwan_rfkill);
664 rfkill_destroy(wwan_rfkill);
665 }
666}
667
427static int dell_send_intensity(struct backlight_device *bd) 668static int dell_send_intensity(struct backlight_device *bd)
428{ 669{
429 int ret = 0; 670 int ret = 0;
@@ -515,6 +756,30 @@ static void touchpad_led_exit(void)
515 led_classdev_unregister(&touchpad_led); 756 led_classdev_unregister(&touchpad_led);
516} 757}
517 758
759static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
760 struct serio *port)
761{
762 static bool extended;
763
764 if (str & 0x20)
765 return false;
766
767 if (unlikely(data == 0xe0)) {
768 extended = true;
769 return false;
770 } else if (unlikely(extended)) {
771 switch (data) {
772 case 0x8:
773 schedule_delayed_work(&dell_rfkill_work,
774 round_jiffies_relative(HZ));
775 break;
776 }
777 extended = false;
778 }
779
780 return false;
781}
782
518static int __init dell_init(void) 783static int __init dell_init(void)
519{ 784{
520 int max_intensity = 0; 785 int max_intensity = 0;
@@ -557,10 +822,26 @@ static int __init dell_init(void)
557 } 822 }
558 buffer = page_address(bufferpage); 823 buffer = page_address(bufferpage);
559 824
825 ret = dell_setup_rfkill();
826
827 if (ret) {
828 pr_warn("Unable to setup rfkill\n");
829 goto fail_rfkill;
830 }
831
832 ret = i8042_install_filter(dell_laptop_i8042_filter);
833 if (ret) {
834 pr_warn("Unable to install key filter\n");
835 goto fail_filter;
836 }
837
560 if (quirks && quirks->touchpad_led) 838 if (quirks && quirks->touchpad_led)
561 touchpad_led_init(&platform_device->dev); 839 touchpad_led_init(&platform_device->dev);
562 840
563 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); 841 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
842 if (dell_laptop_dir != NULL)
843 debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
844 &dell_debugfs_fops);
564 845
565#ifdef CONFIG_ACPI 846#ifdef CONFIG_ACPI
566 /* In the event of an ACPI backlight being available, don't 847 /* In the event of an ACPI backlight being available, don't
@@ -603,6 +884,11 @@ static int __init dell_init(void)
603 return 0; 884 return 0;
604 885
605fail_backlight: 886fail_backlight:
887 i8042_remove_filter(dell_laptop_i8042_filter);
888 cancel_delayed_work_sync(&dell_rfkill_work);
889fail_filter:
890 dell_cleanup_rfkill();
891fail_rfkill:
606 free_page((unsigned long)bufferpage); 892 free_page((unsigned long)bufferpage);
607fail_buffer: 893fail_buffer:
608 platform_device_del(platform_device); 894 platform_device_del(platform_device);
@@ -620,7 +906,10 @@ static void __exit dell_exit(void)
620 debugfs_remove_recursive(dell_laptop_dir); 906 debugfs_remove_recursive(dell_laptop_dir);
621 if (quirks && quirks->touchpad_led) 907 if (quirks && quirks->touchpad_led)
622 touchpad_led_exit(); 908 touchpad_led_exit();
909 i8042_remove_filter(dell_laptop_i8042_filter);
910 cancel_delayed_work_sync(&dell_rfkill_work);
623 backlight_device_unregister(dell_backlight_device); 911 backlight_device_unregister(dell_backlight_device);
912 dell_cleanup_rfkill();
624 if (platform_device) { 913 if (platform_device) {
625 platform_device_unregister(platform_device); 914 platform_device_unregister(platform_device);
626 platform_driver_unregister(&platform_driver); 915 platform_driver_unregister(&platform_driver);