eeepc-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 <mjg@redhat.com> Signed-off-by: Corentin Chary <corentincj@iksaif.net> Tested-by: Darren Salt <linux@youmustbejoking.demon.co.uk> Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
		
					parent
					
						
							
								28d0325ce6
							
						
					
				
			
			
				commit
				
					
						2b121bc262
					
				
			
		
					 2 changed files with 89 additions and 0 deletions
				
			
		| 
						 | 
					@ -357,6 +357,8 @@ config EEEPC_LAPTOP
 | 
				
			||||||
	depends on RFKILL || RFKILL = n
 | 
						depends on RFKILL || RFKILL = n
 | 
				
			||||||
	select BACKLIGHT_CLASS_DEVICE
 | 
						select BACKLIGHT_CLASS_DEVICE
 | 
				
			||||||
	select HWMON
 | 
						select HWMON
 | 
				
			||||||
 | 
						select HOTPLUG
 | 
				
			||||||
 | 
						select HOTPLUG_PCI if PCI
 | 
				
			||||||
	---help---
 | 
						---help---
 | 
				
			||||||
	  This driver supports the Fn-Fx keys on Eee PC laptops.
 | 
						  This driver supports the Fn-Fx keys on Eee PC laptops.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,7 @@
 | 
				
			||||||
#include <linux/input.h>
 | 
					#include <linux/input.h>
 | 
				
			||||||
#include <linux/rfkill.h>
 | 
					#include <linux/rfkill.h>
 | 
				
			||||||
#include <linux/pci.h>
 | 
					#include <linux/pci.h>
 | 
				
			||||||
 | 
					#include <linux/pci_hotplug.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define EEEPC_LAPTOP_VERSION	"0.1"
 | 
					#define EEEPC_LAPTOP_VERSION	"0.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -143,6 +144,7 @@ struct eeepc_hotk {
 | 
				
			||||||
	u16 *keycode_map;
 | 
						u16 *keycode_map;
 | 
				
			||||||
	struct rfkill *eeepc_wlan_rfkill;
 | 
						struct rfkill *eeepc_wlan_rfkill;
 | 
				
			||||||
	struct rfkill *eeepc_bluetooth_rfkill;
 | 
						struct rfkill *eeepc_bluetooth_rfkill;
 | 
				
			||||||
 | 
						struct hotplug_slot *hotplug_slot;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* The actual device the driver binds to */
 | 
					/* The actual device the driver binds to */
 | 
				
			||||||
| 
						 | 
					@ -213,6 +215,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 */
 | 
					/* The backlight device /sys/class/backlight */
 | 
				
			||||||
static struct backlight_device *eeepc_backlight_device;
 | 
					static struct backlight_device *eeepc_backlight_device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -612,6 +623,19 @@ static int notify_brn(void)
 | 
				
			||||||
	return -1;
 | 
						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_hotplug(void)
 | 
					static void eeepc_rfkill_hotplug(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pci_dev *dev;
 | 
						struct pci_dev *dev;
 | 
				
			||||||
| 
						 | 
					@ -744,6 +768,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)
 | 
					static int eeepc_hotk_add(struct acpi_device *device)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int result;
 | 
						int result;
 | 
				
			||||||
| 
						 | 
					@ -802,8 +874,21 @@ static int eeepc_hotk_add(struct acpi_device *device)
 | 
				
			||||||
			goto bluetooth_fail;
 | 
								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;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 pci_fail:
 | 
				
			||||||
 | 
						if (ehotk->eeepc_bluetooth_rfkill)
 | 
				
			||||||
 | 
							rfkill_unregister(ehotk->eeepc_bluetooth_rfkill);
 | 
				
			||||||
 bluetooth_fail:
 | 
					 bluetooth_fail:
 | 
				
			||||||
	rfkill_destroy(ehotk->eeepc_bluetooth_rfkill);
 | 
						rfkill_destroy(ehotk->eeepc_bluetooth_rfkill);
 | 
				
			||||||
	rfkill_unregister(ehotk->eeepc_wlan_rfkill);
 | 
						rfkill_unregister(ehotk->eeepc_wlan_rfkill);
 | 
				
			||||||
| 
						 | 
					@ -825,6 +910,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.P0P6");
 | 
				
			||||||
	eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
 | 
						eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
 | 
				
			||||||
 | 
						if (ehotk->hotplug_slot)
 | 
				
			||||||
 | 
							pci_hp_deregister(ehotk->hotplug_slot);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kfree(ehotk);
 | 
						kfree(ehotk);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue