diff options
author | Matthew Garrett <mjg@redhat.com> | 2009-01-20 10:17:48 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2009-01-20 14:36:53 -0500 |
commit | 5740294ca3a9b113fe146f2826effb69ca50008d (patch) | |
tree | 00e571fce1111310ef4e12801a657aeaf80ae77f /drivers/platform/x86 | |
parent | c9ddf8fede1271bde0a512fa94f77c4cb1ef4040 (diff) |
eeepc-laptop: Implement rfkill hotplugging in eeepc-laptop
The Eee implements rfkill by logically unplugging the wireless card from the
PCI bus. Despite sending ACPI notifications, this does not appear to be
implemented using standard ACPI hotplug - nor does the firmware provide the
_OSC method required to support native PCIe hotplug. The only sensible choice
appears to be to handle the hotplugging directly in the eeepc-laptop driver.
Tested successfully on a 700, 900 and 901.
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Corentin Chary <corentincj@iksaif.net>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 21e5206ed28b..66655d2b4ce9 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/uaccess.h> | 30 | #include <linux/uaccess.h> |
31 | #include <linux/input.h> | 31 | #include <linux/input.h> |
32 | #include <linux/rfkill.h> | 32 | #include <linux/rfkill.h> |
33 | #include <linux/pci.h> | ||
33 | 34 | ||
34 | #define EEEPC_LAPTOP_VERSION "0.1" | 35 | #define EEEPC_LAPTOP_VERSION "0.1" |
35 | 36 | ||
@@ -517,6 +518,41 @@ static void notify_brn(void) | |||
517 | bd->props.brightness = read_brightness(bd); | 518 | bd->props.brightness = read_brightness(bd); |
518 | } | 519 | } |
519 | 520 | ||
521 | static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) | ||
522 | { | ||
523 | struct pci_dev *dev; | ||
524 | struct pci_bus *bus = pci_find_bus(0, 1); | ||
525 | |||
526 | if (event != ACPI_NOTIFY_BUS_CHECK) | ||
527 | return; | ||
528 | |||
529 | if (!bus) { | ||
530 | printk(EEEPC_WARNING "Unable to find PCI bus 1?\n"); | ||
531 | return; | ||
532 | } | ||
533 | |||
534 | if (get_acpi(CM_ASL_WLAN) == 1) { | ||
535 | dev = pci_get_slot(bus, 0); | ||
536 | if (dev) { | ||
537 | /* Device already present */ | ||
538 | pci_dev_put(dev); | ||
539 | return; | ||
540 | } | ||
541 | dev = pci_scan_single_device(bus, 0); | ||
542 | if (dev) { | ||
543 | pci_bus_assign_resources(bus); | ||
544 | if (pci_bus_add_device(dev)) | ||
545 | printk(EEEPC_ERR "Unable to hotplug wifi\n"); | ||
546 | } | ||
547 | } else { | ||
548 | dev = pci_get_slot(bus, 0); | ||
549 | if (dev) { | ||
550 | pci_remove_bus_device(dev); | ||
551 | pci_dev_put(dev); | ||
552 | } | ||
553 | } | ||
554 | } | ||
555 | |||
520 | static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) | 556 | static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) |
521 | { | 557 | { |
522 | static struct key_entry *key; | 558 | static struct key_entry *key; |
@@ -543,6 +579,45 @@ static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) | |||
543 | } | 579 | } |
544 | } | 580 | } |
545 | 581 | ||
582 | static int eeepc_register_rfkill_notifier(char *node) | ||
583 | { | ||
584 | acpi_status status = AE_OK; | ||
585 | acpi_handle handle; | ||
586 | |||
587 | status = acpi_get_handle(NULL, node, &handle); | ||
588 | |||
589 | if (ACPI_SUCCESS(status)) { | ||
590 | status = acpi_install_notify_handler(handle, | ||
591 | ACPI_SYSTEM_NOTIFY, | ||
592 | eeepc_rfkill_notify, | ||
593 | NULL); | ||
594 | if (ACPI_FAILURE(status)) | ||
595 | printk(EEEPC_WARNING | ||
596 | "Failed to register notify on %s\n", node); | ||
597 | } else | ||
598 | return -ENODEV; | ||
599 | |||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | static void eeepc_unregister_rfkill_notifier(char *node) | ||
604 | { | ||
605 | acpi_status status = AE_OK; | ||
606 | acpi_handle handle; | ||
607 | |||
608 | status = acpi_get_handle(NULL, node, &handle); | ||
609 | |||
610 | if (ACPI_SUCCESS(status)) { | ||
611 | status = acpi_remove_notify_handler(handle, | ||
612 | ACPI_SYSTEM_NOTIFY, | ||
613 | eeepc_rfkill_notify); | ||
614 | if (ACPI_FAILURE(status)) | ||
615 | printk(EEEPC_ERR | ||
616 | "Error removing rfkill notify handler %s\n", | ||
617 | node); | ||
618 | } | ||
619 | } | ||
620 | |||
546 | static int eeepc_hotk_add(struct acpi_device *device) | 621 | static int eeepc_hotk_add(struct acpi_device *device) |
547 | { | 622 | { |
548 | acpi_status status = AE_OK; | 623 | acpi_status status = AE_OK; |
@@ -622,6 +697,10 @@ static int eeepc_hotk_add(struct acpi_device *device) | |||
622 | if (result) | 697 | if (result) |
623 | goto bluetooth_fail; | 698 | goto bluetooth_fail; |
624 | } | 699 | } |
700 | |||
701 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); | ||
702 | eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); | ||
703 | |||
625 | return 0; | 704 | return 0; |
626 | 705 | ||
627 | bluetooth_fail: | 706 | bluetooth_fail: |
@@ -649,6 +728,10 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) | |||
649 | eeepc_hotk_notify); | 728 | eeepc_hotk_notify); |
650 | if (ACPI_FAILURE(status)) | 729 | if (ACPI_FAILURE(status)) |
651 | printk(EEEPC_ERR "Error removing notify handler\n"); | 730 | printk(EEEPC_ERR "Error removing notify handler\n"); |
731 | |||
732 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); | ||
733 | eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); | ||
734 | |||
652 | kfree(ehotk); | 735 | kfree(ehotk); |
653 | return 0; | 736 | return 0; |
654 | } | 737 | } |