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>
This commit is contained in:
Matthew Garrett 2009-01-20 16:17:48 +01:00 committed by Len Brown
parent c9ddf8fede
commit 5740294ca3

View file

@ -30,6 +30,7 @@
#include <linux/uaccess.h>
#include <linux/input.h>
#include <linux/rfkill.h>
#include <linux/pci.h>
#define EEEPC_LAPTOP_VERSION "0.1"
@ -517,6 +518,41 @@ static void notify_brn(void)
bd->props.brightness = read_brightness(bd);
}
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
struct pci_dev *dev;
struct pci_bus *bus = pci_find_bus(0, 1);
if (event != ACPI_NOTIFY_BUS_CHECK)
return;
if (!bus) {
printk(EEEPC_WARNING "Unable to find PCI bus 1?\n");
return;
}
if (get_acpi(CM_ASL_WLAN) == 1) {
dev = pci_get_slot(bus, 0);
if (dev) {
/* Device already present */
pci_dev_put(dev);
return;
}
dev = pci_scan_single_device(bus, 0);
if (dev) {
pci_bus_assign_resources(bus);
if (pci_bus_add_device(dev))
printk(EEEPC_ERR "Unable to hotplug wifi\n");
}
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
pci_remove_bus_device(dev);
pci_dev_put(dev);
}
}
}
static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
{
static struct key_entry *key;
@ -543,6 +579,45 @@ static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
}
}
static int eeepc_register_rfkill_notifier(char *node)
{
acpi_status status = AE_OK;
acpi_handle handle;
status = acpi_get_handle(NULL, node, &handle);
if (ACPI_SUCCESS(status)) {
status = acpi_install_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
eeepc_rfkill_notify,
NULL);
if (ACPI_FAILURE(status))
printk(EEEPC_WARNING
"Failed to register notify on %s\n", node);
} else
return -ENODEV;
return 0;
}
static void eeepc_unregister_rfkill_notifier(char *node)
{
acpi_status status = AE_OK;
acpi_handle handle;
status = acpi_get_handle(NULL, node, &handle);
if (ACPI_SUCCESS(status)) {
status = acpi_remove_notify_handler(handle,
ACPI_SYSTEM_NOTIFY,
eeepc_rfkill_notify);
if (ACPI_FAILURE(status))
printk(EEEPC_ERR
"Error removing rfkill notify handler %s\n",
node);
}
}
static int eeepc_hotk_add(struct acpi_device *device)
{
acpi_status status = AE_OK;
@ -622,6 +697,10 @@ static int eeepc_hotk_add(struct acpi_device *device)
if (result)
goto bluetooth_fail;
}
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
return 0;
bluetooth_fail:
@ -649,6 +728,10 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type)
eeepc_hotk_notify);
if (ACPI_FAILURE(status))
printk(EEEPC_ERR "Error removing notify handler\n");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
kfree(ehotk);
return 0;
}