2009-04-19 01:56:33

by Matthew Garrett

[permalink] [raw]
Subject: [PATCH] eee-laptop: Register as a pci-hotplug device

eee-laptop: Register as a pci-hotplug device

The eee contains a logically (but not physically) hotpluggable PCIe slot.
Currently this is handled by adding or removing the PCI device in response
to rfkill events, but if a user has forced pciehp to bind to it (with the
force=1 argument) then both drivers will try to handle the event and
hilarity (in the form of oopses) will ensue. This can be avoided by having
eee-laptop register the slot as a hotplug slot. Only one of pciehp and
eee-laptop will successfully register this, avoiding the problem.

Signed-off-by: Matthew Garrett <[email protected]>
Tested-by: Darren Salt <[email protected]>

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 284ebac..afbe441 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -341,6 +341,7 @@ config EEEPC_LAPTOP
select BACKLIGHT_CLASS_DEVICE
select HWMON
select RFKILL
+ select HOTPLUG_PCI
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
It also adds the ability to switch camera/wlan on/off.
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 6f54fd1..7039dc9 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -31,6 +31,7 @@
#include <linux/input.h>
#include <linux/rfkill.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>

#define EEEPC_LAPTOP_VERSION "0.1"

@@ -132,6 +133,7 @@ struct eeepc_hotk {
u16 *keycode_map;
struct rfkill *eeepc_wlan_rfkill;
struct rfkill *eeepc_bluetooth_rfkill;
+ struct hotplug_slot *hotplug_slot;
};

/* The actual device the driver binds to */
@@ -194,6 +196,15 @@ static struct acpi_driver eeepc_hotk_driver = {
},
};

+/* PCI hotplug ops */
+static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .get_adapter_status = eeepc_get_adapter_status,
+ .get_power_status = eeepc_get_adapter_status,
+};
+
/* The backlight device /sys/class/backlight */
static struct backlight_device *eeepc_backlight_device;

@@ -519,6 +530,19 @@ static void notify_brn(void)
bd->props.brightness = read_brightness(bd);
}

+static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
+ u8 *value)
+{
+ int val = get_acpi(CM_ASL_WLAN);
+
+ if (val == 1 || val == 0)
+ *value = val;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
struct pci_dev *dev;
@@ -624,6 +648,54 @@ static void eeepc_unregister_rfkill_notifier(char *node)
}
}

+static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
+{
+ kfree(hotplug_slot->info);
+ kfree(hotplug_slot);
+}
+
+static int eeepc_setup_pci_hotplug(void)
+{
+ int ret = -ENOMEM;
+ struct pci_bus *bus = pci_find_bus(0, 1);
+
+ if (!bus) {
+ printk(EEEPC_ERR "Unable to find wifi PCI bus\n");
+ return -ENODEV;
+ }
+
+ ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+ if (!ehotk->hotplug_slot)
+ goto error_slot;
+
+ ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
+ GFP_KERNEL);
+ if (!ehotk->hotplug_slot->info)
+ goto error_info;
+
+ ehotk->hotplug_slot->private = ehotk;
+ ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
+ ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
+ eeepc_get_adapter_status(ehotk->hotplug_slot,
+ &ehotk->hotplug_slot->info->adapter_status);
+
+ ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
+ if (ret) {
+ printk(EEEPC_ERR "Unable to register hotplug slot - %d\n", ret);
+ goto error_register;
+ }
+
+ return 0;
+
+error_register:
+ kfree(ehotk->hotplug_slot->info);
+error_info:
+ kfree(ehotk->hotplug_slot);
+ ehotk->hotplug_slot = NULL;
+error_slot:
+ return ret;
+}
+
static int eeepc_hotk_add(struct acpi_device *device)
{
acpi_status status = AE_OK;
@@ -704,11 +776,25 @@ static int eeepc_hotk_add(struct acpi_device *device)
goto bluetooth_fail;
}

+ result = eeepc_setup_pci_hotplug();
+
+ /*
+ * If we get -EBUSY then something else is handling the PCI hotplug -
+ * don't fail in this case
+ */
+
+ if (result == -EBUSY)
+ return 0;
+ else if (result)
+ goto pci_fail;
+
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");

return 0;
-
+ pci_fail:
+ if (ehotk->eeepc_bluetooth_rfkill)
+ rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
bluetooth_fail:
if (ehotk->eeepc_bluetooth_rfkill)
rfkill_free(ehotk->eeepc_bluetooth_rfkill);
@@ -735,8 +821,11 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type)
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");
+ if (ehotk->hotplug_slot) {
+ eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
+ eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
+ pci_hp_deregister(ehotk->hotplug_slot);
+ }

kfree(ehotk);
return 0;
--
Matthew Garrett | [email protected]


2009-04-19 07:20:26

by Corentin Chary

[permalink] [raw]
Subject: Re: [PATCH] eee-laptop: Register as a pci-hotplug device

> + result = eeepc_setup_pci_hotplug();
> +
> + /*
> + * If we get -EBUSY then something else is handling the PCI hotplug -
> + * don't fail in this case
> + */
> +
> + if (result == -EBUSY)
> + return 0;
> + else if (result)
> + goto pci_fail;
> +
> eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
> eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
>

> - eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
> - eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
> + if (ehotk->hotplug_slot) {
> + eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
> + eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
> + pci_hp_deregister(ehotk->hotplug_slot);
> + }

Hi,
This patch conflict with
http://git.iksaif.net/?p=acpi4asus.git;a=commitdiff;h=8082f39bb3d4a27c7bb03a76e72c70c86d55117b;hp=7ec0a7290797f57b780f792d12f4bcc19c83aa4f
A quick fix is to do :

eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
if (ehotk->hotplug_slot)
pci_hp_deregister(ehotk->hotplug_slot);

but I'm not sure it's correct.
I should have sent a series sooner to avoid that :/
Thanks

2009-04-19 15:13:22

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH] eee-laptop: Register as a pci-hotplug device

On Sun, Apr 19, 2009 at 09:20:07AM +0200, Corentin Chary wrote:

> Hi,
> This patch conflict with
> http://git.iksaif.net/?p=acpi4asus.git;a=commitdiff;h=8082f39bb3d4a27c7bb03a76e72c70c86d55117b;hp=7ec0a7290797f57b780f792d12f4bcc19c83aa4f
> A quick fix is to do :
>
> eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
> eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
> if (ehotk->hotplug_slot)
> pci_hp_deregister(ehotk->hotplug_slot);
>
> but I'm not sure it's correct.
> I should have sent a series sooner to avoid that :/

Ok - I'll grab that and rediff.

--
Matthew Garrett | [email protected]

2009-04-25 14:12:44

by Corentin Chary

[permalink] [raw]
Subject: Re: [PATCH] eee-laptop: Register as a pci-hotplug device

Hi,
I take your patch and make it works against acpi4asus tree.
If you think it's ok, then I'll send a serie including this patch to Len.
(the tree is at http://git.iksaif.net/ )

From: Matthew Garrett <[email protected]>

The eee contains a logically (but not physically) hotpluggable PCIe slot.
Currently this is handled by adding or removing the PCI device in response
to rfkill events, but if a user has forced pciehp to bind to it (with the
force=1 argument) then both drivers will try to handle the event and
hilarity (in the form of oopses) will ensue. This can be avoided by having
eee-laptop register the slot as a hotplug slot. Only one of pciehp and
eee-laptop will successfully register this, avoiding the problem.

Signed-off-by: Matthew Garrett <[email protected]>
Signed-off-by: Corentin Chary <[email protected]>
Tested-by: Darren Salt <[email protected]>
---
drivers/platform/x86/Kconfig | 1 +
drivers/platform/x86/eeepc-laptop.c | 87 +++++++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 284ebac..afbe441 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -341,6 +341,7 @@ config EEEPC_LAPTOP
select BACKLIGHT_CLASS_DEVICE
select HWMON
select RFKILL
+ select HOTPLUG_PCI
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
It also adds the ability to switch camera/wlan on/off.
diff --git a/drivers/platform/x86/eeepc-laptop.c
b/drivers/platform/x86/eeepc-laptop.c
index 7aaf587..4440f6c 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -31,6 +31,7 @@
#include <linux/input.h>
#include <linux/rfkill.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>

#define EEEPC_LAPTOP_VERSION "0.1"

@@ -132,6 +133,7 @@ struct eeepc_hotk {
u16 *keycode_map;
struct rfkill *eeepc_wlan_rfkill;
struct rfkill *eeepc_bluetooth_rfkill;
+ struct hotplug_slot *hotplug_slot;
};

/* The actual device the driver binds to */
@@ -197,6 +199,15 @@ static struct acpi_driver eeepc_hotk_driver = {
},
};

+/* PCI hotplug ops */
+static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .get_adapter_status = eeepc_get_adapter_status,
+ .get_power_status = eeepc_get_adapter_status,
+};
+
/* The backlight device /sys/class/backlight */
static struct backlight_device *eeepc_backlight_device;

@@ -529,6 +540,19 @@ static int notify_brn(void)
return -1;
}

+static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
+ u8 *value)
+{
+ int val = get_acpi(CM_ASL_WLAN);
+
+ if (val == 1 || val == 0)
+ *value = val;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
enum rfkill_state state;
@@ -655,6 +679,54 @@ static void eeepc_unregister_rfkill_notifier(char *node)
}
}

+static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
+{
+ kfree(hotplug_slot->info);
+ kfree(hotplug_slot);
+}
+
+static int eeepc_setup_pci_hotplug(void)
+{
+ int ret = -ENOMEM;
+ struct pci_bus *bus = pci_find_bus(0, 1);
+
+ if (!bus) {
+ printk(EEEPC_ERR "Unable to find wifi PCI bus\n");
+ return -ENODEV;
+ }
+
+ ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+ if (!ehotk->hotplug_slot)
+ goto error_slot;
+
+ ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
+ GFP_KERNEL);
+ if (!ehotk->hotplug_slot->info)
+ goto error_info;
+
+ ehotk->hotplug_slot->private = ehotk;
+ ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
+ ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
+ eeepc_get_adapter_status(ehotk->hotplug_slot,
+ &ehotk->hotplug_slot->info->adapter_status);
+
+ ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
+ if (ret) {
+ printk(EEEPC_ERR "Unable to register hotplug slot - %d\n", ret);
+ goto error_register;
+ }
+
+ return 0;
+
+error_register:
+ kfree(ehotk->hotplug_slot->info);
+error_info:
+ kfree(ehotk->hotplug_slot);
+ ehotk->hotplug_slot = NULL;
+error_slot:
+ return ret;
+}
+
static int eeepc_hotk_add(struct acpi_device *device)
{
acpi_status status = AE_OK;
@@ -738,8 +810,21 @@ static int eeepc_hotk_add(struct acpi_device *device)
goto bluetooth_fail;
}

+ result = eeepc_setup_pci_hotplug();
+ /*
+ * If we get -EBUSY then something else is handling the PCI hotplug -
+ * don't fail in this case
+ */
+ if (result == -EBUSY)
+ return 0;
+ else if (result)
+ goto pci_fail;
+
return 0;

+ pci_fail:
+ if (ehotk->eeepc_bluetooth_rfkill)
+ rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
bluetooth_fail:
if (ehotk->eeepc_bluetooth_rfkill)
rfkill_free(ehotk->eeepc_bluetooth_rfkill);
@@ -768,6 +853,8 @@ static int eeepc_hotk_remove(struct acpi_device
*device, int type)

eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
+ if (ehotk->hotplug_slot)
+ pci_hp_deregister(ehotk->hotplug_slot);

kfree(ehotk);
return 0;
--
1.6.2.3

2009-04-26 17:16:55

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH] eee-laptop: Register as a pci-hotplug device

Hm. I /think/ that version leaks notification handlers on the failure
path. Does this look ok? I don't have an eee to hand right now, so can't
test this at the moment.

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 284ebac..afbe441 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -341,6 +341,7 @@ config EEEPC_LAPTOP
select BACKLIGHT_CLASS_DEVICE
select HWMON
select RFKILL
+ select HOTPLUG_PCI
---help---
This driver supports the Fn-Fx keys on Eee PC laptops.
It also adds the ability to switch camera/wlan on/off.
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index 7aaf587..deaf3c3 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -31,6 +31,7 @@
#include <linux/input.h>
#include <linux/rfkill.h>
#include <linux/pci.h>
+#include <linux/pci_hotplug.h>

#define EEEPC_LAPTOP_VERSION "0.1"

@@ -132,6 +133,7 @@ struct eeepc_hotk {
u16 *keycode_map;
struct rfkill *eeepc_wlan_rfkill;
struct rfkill *eeepc_bluetooth_rfkill;
+ struct hotplug_slot *hotplug_slot;
};

/* The actual device the driver binds to */
@@ -197,6 +199,15 @@ static struct acpi_driver eeepc_hotk_driver = {
},
};

+/* PCI hotplug ops */
+static int eeepc_get_adapter_status(struct hotplug_slot *slot, u8 *value);
+
+static struct hotplug_slot_ops eeepc_hotplug_slot_ops = {
+ .owner = THIS_MODULE,
+ .get_adapter_status = eeepc_get_adapter_status,
+ .get_power_status = eeepc_get_adapter_status,
+};
+
/* The backlight device /sys/class/backlight */
static struct backlight_device *eeepc_backlight_device;

@@ -529,6 +540,19 @@ static int notify_brn(void)
return -1;
}

+static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot,
+ u8 *value)
+{
+ int val = get_acpi(CM_ASL_WLAN);
+
+ if (val == 1 || val == 0)
+ *value = val;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
{
enum rfkill_state state;
@@ -655,6 +679,54 @@ static void eeepc_unregister_rfkill_notifier(char *node)
}
}

+static void eeepc_cleanup_pci_hotplug(struct hotplug_slot *hotplug_slot)
+{
+ kfree(hotplug_slot->info);
+ kfree(hotplug_slot);
+}
+
+static int eeepc_setup_pci_hotplug(void)
+{
+ int ret = -ENOMEM;
+ struct pci_bus *bus = pci_find_bus(0, 1);
+
+ if (!bus) {
+ printk(EEEPC_ERR "Unable to find wifi PCI bus\n");
+ return -ENODEV;
+ }
+
+ ehotk->hotplug_slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+ if (!ehotk->hotplug_slot)
+ goto error_slot;
+
+ ehotk->hotplug_slot->info = kzalloc(sizeof(struct hotplug_slot_info),
+ GFP_KERNEL);
+ if (!ehotk->hotplug_slot->info)
+ goto error_info;
+
+ ehotk->hotplug_slot->private = ehotk;
+ ehotk->hotplug_slot->release = &eeepc_cleanup_pci_hotplug;
+ ehotk->hotplug_slot->ops = &eeepc_hotplug_slot_ops;
+ eeepc_get_adapter_status(ehotk->hotplug_slot,
+ &ehotk->hotplug_slot->info->adapter_status);
+
+ ret = pci_hp_register(ehotk->hotplug_slot, bus, 0, "eeepc-wifi");
+ if (ret) {
+ printk(EEEPC_ERR "Unable to register hotplug slot - %d\n", ret);
+ goto error_register;
+ }
+
+ return 0;
+
+error_register:
+ kfree(ehotk->hotplug_slot->info);
+error_info:
+ kfree(ehotk->hotplug_slot);
+ ehotk->hotplug_slot = NULL;
+error_slot:
+ return ret;
+}
+
static int eeepc_hotk_add(struct acpi_device *device)
{
acpi_status status = AE_OK;
@@ -738,8 +810,21 @@ static int eeepc_hotk_add(struct acpi_device *device)
goto bluetooth_fail;
}

+ result = eeepc_setup_pci_hotplug();
+ /*
+ * If we get -EBUSY then something else is handling the PCI hotplug -
+ * don't fail in this case
+ */
+ if (result == -EBUSY)
+ return 0;
+ else if (result)
+ goto pci_fail;
+
return 0;

+ pci_fail:
+ if (ehotk->eeepc_bluetooth_rfkill)
+ rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
bluetooth_fail:
if (ehotk->eeepc_bluetooth_rfkill)
rfkill_free(ehotk->eeepc_bluetooth_rfkill);
@@ -748,6 +833,10 @@ static int eeepc_hotk_add(struct acpi_device *device)
wlan_fail:
if (ehotk->eeepc_wlan_rfkill)
rfkill_free(ehotk->eeepc_wlan_rfkill);
+ eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
+ eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
+ acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
+ eeepc_hotk_notify);
ehotk_fail:
kfree(ehotk);
ehotk = NULL;
@@ -768,6 +857,8 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type)

eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
+ if (ehotk->hotplug_slot)
+ pci_hp_deregister(ehotk->hotplug_slot);

kfree(ehotk);
return 0;

--
Matthew Garrett | [email protected]

2009-04-26 20:58:24

by Corentin Chary

[permalink] [raw]
Subject: Re: [PATCH] eee-laptop: Register as a pci-hotplug device

On Sun, Apr 26, 2009 at 7:16 PM, Matthew Garrett <[email protected]> wrote:
> Hm. I /think/ that version leaks notification handlers on the failure
> path. Does this look ok? I don't have an eee to hand right now, so can't
> test this at the moment.
I'll test it tomorow, but it seems ok.
Thanks

--
Corentin Chary
http://xf.iksaif.net